大家好,我是前端西瓜哥。今天我们来学习使用 canvas 技术实现图片查看器需要掌握的一个知识点。
需要在一个特定大小的容器内加载并展示一张照片,我们可以怎样进行图片的默认展示?
contain
contain,将图片保持宽高比缩放到刚好能够放入容器,是最常用的方案。
优点是可以一次看到整张图片,缺点是不能填充整个容器,因此会产生 “黑边”。
这里我们假设画布宽高为 400 x 200,图片宽高为 80 x 80。
先看看示例的模板代码。
const canvas = document.querySelector('canvas'); canvas.width = 400; canvas.height = 200; const ctx = canvas.getContext('2d'); ctx.fillRect(0, 0, canvas.width, canvas.height); // 填充黑色底色 const img = new Image(); img.src = './watermelon.jpg'; // 图片大小为 80x80 img.onload = showImg; function showImg() { // 这里选择了 “contain” 方案 const scale = calcContainScale(img.width, img.height, canvas.width, canvas.height); const w = img.width * scale; // 图片缩放后的宽度 const h = img.height * scale; // 图片缩放后的高度 const {x, y} = calcPos(w, h, canvas.width, canvas.height) // 顺便让图片居中 ctx.drawImage(img, x, y, w, h); } // 计算让图片居中需要设置的 x,y function calcPos(w, h, cw, ch) { return { x: (cw - w) / 2, y: (ch - h) / 2 }; }
我们的 calcContainScale()
方法实现为:
/** * contain 模式 * @param {number} w 图片宽度 * @param {number} h 图片高度 * @param {number} cw 容器宽度 * @param {number} ch 容器高度 * @returns {number} 缩放比 */ function calcContainScale(w, h, cw, ch) { const scaleW = cw / w; const scaleH = ch / h; const scale = Math.min(scaleW, scaleH); // 取小值 return scale; }
算法很简单:计算出将图片的宽高分别缩放为容器宽高需要的比例,然后取其中小的即可。
原因很简单,如果你取大的,必然导致另一个长度超过容器尺寸,图片无法被装下。
cover
cover,图片保持宽高比并尽可能充满容器,需要图片的宽缩放为容器宽,或图片的高缩放为容器宽,然后多余的内容截断。
优点是可以用最小的缩放比来填充容器所有的地方,缺点是不能展示图片所有的内容。
function calcCoverScale(w, h, cw, ch) { const scaleW = cw / w; const scaleH = ch / h; const scale = Math.max(scaleW, scaleH); // 取大值 return scale; }
和 contain 相反,算出将图片的宽高分别缩放为容器宽高需要的比例之后,要取其中的大值。
因为我们是要尽可能填充容器,另一个边如果超出了容器范围,就将其截断。
fill
fill,图片完全填充容器,不要求保持宽高比,可以对图片进行拉伸。
非常少用,因为会将图片拉伸,导致图片非常难看。
实现上,只要直接将图片的宽高设置为容器的宽高即可,不需要用到什么计算。
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
none
none,图片保持原本大小、不进行缩放地显示。
ctx.drawImage(img, x, y, img.width, img.height);
scale-down
scale-down,应用 contain 和 none 中图片尺寸较小的。
这么做是希望尺寸小于容器的图片能保持原比例,而不是被放大导致失真。
function calcScaleDownScale(w, h, cw, ch) { const scaleW = cw / w; const scaleH = ch / h; const scale = Math.min(scaleW, scaleH, 1); // 比例不能小于 1 return scale; }
图片尺寸远小于容器,最终选择是 none 的方式。
总结
西瓜哥我总结一下这 5 种图片填充容器的方式:
1.contain:刚好完整放入容器,不多也不少;2.cover:充满容器,但尺寸尽量小;3.fill:不保持宽高比,直接拉伸填充容器;4.none:保持图片最初的模样;5.scale-down:如果可以不放大就能放入容器,直接放入(none);如果不能,就放大点,让其刚好放入容器(contain)