1. 前言

我们常用到 CSS 的 background-size 属性中的 containcover 属性,来实现背景图的适配。本文将要介绍在用 canvas 绘制图片的时候,如何实现 containcover 效果呢?

2. contain 概念

缩放背景图片以完全装入背景区,可能背景区部分空白。contain 尽可能的缩放背景并保持图像的宽高比例(图像不会被压缩)。该背景图会填充所在的容器。当背景图和容器的大小不同时,容器的空白区域(上、下或者左、右)会显示由 background-color 设置的背景颜色。

说白了就是保持图片宽高比并保证图片的长边能完整显示,用到 canvas 的 drawImage(img, dx, dy, dw, dh) 方法:

  • 参数 dxdy 分别表示图片左上角顶点的横、纵坐标;
  • 参数 dwdh 分别表示定义图片的宽、高。

2.1 公式推导

需要分两种情况:

  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
  2. 当图片的宽高比 >= 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) 方法:

  • 参数 sxsy 分别表示源图片被截取部分左上角顶点的横、纵坐标;
  • 参数 swsh 分别表示源图片被截取部分的宽、高;
  • 参数 dxdydwdh 分别表示切片后将要在画布上绘制的左上角顶点坐标及绘制的宽高。

3.1 公式推导

同样分两种情况:

  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
  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宽高 
    };

原文链接:canvas drawImage 绘图实现 contain 和 cover 的效果