1. 前言
我们常用到 CSS 的 background-size
属性中的 contain
和 cover
属性,来实现背景图的适配。本文将要介绍在用 canvas 绘制图片的时候,如何实现 contain
或 cover
效果呢?
2. contain 概念
缩放背景图片以完全装入背景区,可能背景区部分空白。
contain
尽可能的缩放背景并保持图像的宽高比例(图像不会被压缩)。该背景图会填充所在的容器。当背景图和容器的大小不同时,容器的空白区域(上、下或者左、右)会显示由background-color
设置的背景颜色。
说白了就是保持图片宽高比并保证图片的长边能完整显示,用到 canvas 的 drawImage(img, dx, dy, dw, dh)
方法:
- 参数
dx
,dy
分别表示图片左上角顶点的横、纵坐标; - 参数
dw
,dh
分别表示定义图片的宽、高。
2.1 公式推导
需要分两种情况:
-
当图片宽高比 <= 画布宽高比时,如图:
已知:imgRatio = imgWidth / imgHeight canvasRatio = canvasWidth / canvasHeight
由图得出条件:
imgHeight = canvasHeight
推导:
imgWidth = imgRatio * imgHeight = imgRatio * canvasHeight
即:
dw = imgRatio * canvasHeight dh = canvasHeight dx = (canvasWidth - imgWidth) / 2 dy = 0
-
当图片的宽高比 >= canvas画布宽高比时, 如图:
同理推导出:imgWidth = canvasWidth imgHeight = imgWidth / imgRatio = canvasWidth / imgRatio
即:
dw = canvasWidth dh = dw / imgRatio dx = 0 dy = (canvasHeight - dh) / 2
所以
contain
方式适应画布的代码为:var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); var img = new Image(); img.src = "images/pic1.jpg"; let dx, dy, dw, dh, imgRatio, canvasRatio; canvasRatio = canvas.width / canvas.height; // contain 方式 img.onload = function () { imgRatio = img.width / img.height; if (imgRatio <= canvasRatio) { dw = imgRatio * canvas.width; dh = canvas.height; dx = (canvas.width - dw) / 2; dy = 0; } else { dw = canvas.width; dh = dw / imgRatio; dx = 0; dy = (canvas.height - dh) / 2; } ctx.drawImage(img, dx, dy, dw, dh); };
3. cover 概念
缩放背景图片以完全覆盖背景区,可能背景图片部分看不见。和
contain
值相反,cover
值尽可能大的缩放背景图像并保持图像的宽高比例(图像不会被压扁)。该背景图以它的全部宽或者高覆盖所在容器。当容器和背景图大小不同时,背景图的左、右或者上、下部分会被裁剪。
说白了就是保持图片宽高比的同时保证图片的短边能完全显示出来,因为可能会有裁剪,所以用到 canvas 的 drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh)
方法:
- 参数
sx
,sy
分别表示源图片被截取部分左上角顶点的横、纵坐标; - 参数
sw
,sh
分别表示源图片被截取部分的宽、高; - 参数
dx
,dy
,dw
,dh
分别表示切片后将要在画布上绘制的左上角顶点坐标及绘制的宽高。
3.1 公式推导
同样分两种情况:
- 图片宽高比 <= 画布宽高比时, 如图:
已知:imgRatio = imgWidth / imgHeight canvasRatio = canvasWidth / canvasHeight
由图知:
dw = canvasWidth dh = canvasHeight dx = 0 dy = 0
此时图片的宽要在画布完整展示,所以源图片要裁剪的区域为:
sw = imgWidth sh = sw / canvasRatio sx = 0 sy = (imgHeight - sh) / 2
-
当图片宽高比 >= 画布宽高比时, 如图:
同理,此时图片的高要在画布完整展示,源图片的裁剪区域为:sh = imgHeight sw = sh * canvasRatio sx = (imgWidth - sw) / 2 sy = 0
所以
cover
方式适应画布的代码为:var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); var img = new Image(); img.src = "images/pic1.jpg"; let sx, sy, sw, sh, imgRatio, canvasRatio; canvasRatio = canvas.width / canvas.height; // cover 方式 img.onload = function () { imgRatio = img.width / img.height; if (imgRatio <= canvasRatio) { sw = img.width; sh = sw / canvasRatio; sx = 0; sy = (img.height - sh) / 2; } else { sh = img.height; sw = sh * canvasRatio; sx = (img.width - sw) / 2; sy = 0; } ctx.drawImage(img, sx, sy, sw, sh, 0, 0, canvas.width, canvas.height); // 因为是cover覆盖, 所以dx,dy都是0,绘制宽高即为canvas宽高 };