这是我参与2022首次更文挑战的第15天,活动详情查看:2022首次更文挑战
大家好,我是前端西瓜哥。今天我们来学习使用 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 种图片填充容器的方式:
- contain:刚好完整放入容器,不多也不少;
- cover:充满容器,但尺寸尽量小;
- fill:不保持宽高比,直接拉伸填充容器;
- none:保持图片最初的模样;
- scale-down:如果可以不放大就能放入容器,直接放入(none);如果不能,就放大点,让其刚好放入容器(contain)