在容器内显示图片的五种方案:contain、cover、fill、none、scale-down

大家好,我是前端西瓜哥。今天我们来学习使用 canvas 技术实现图片查看器需要掌握的一个知识点。

需要在一个特定大小的容器内加载并展示一张照片,我们可以怎样进行图片的默认展示?

szYr5U

contain

contain,将图片保持宽高比缩放到刚好能够放入容器,是最常用的方案。

优点是可以一次看到整张图片,缺点是不能填充整个容器,因此会产生 “黑边”。

这里我们假设画布宽高为 400 x 200,图片宽高为 80 x 80。

先看看示例的模板代码。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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() 方法实现为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/**
 * 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;
}

算法很简单:计算出将图片的宽高分别缩放为容器宽高需要的比例,然后取其中小的即可。

原因很简单,如果你取大的,必然导致另一个长度超过容器尺寸,图片无法被装下。

Zq4Ibu

cover

cover,图片保持宽高比并尽可能充满容器,需要图片的宽缩放为容器宽,或图片的高缩放为容器宽,然后多余的内容截断。

优点是可以用最小的缩放比来填充容器所有的地方,缺点是不能展示图片所有的内容。

1
2
3
4
5
6
function calcCoverScale(w, h, cw, ch) {
  const scaleW = cw / w;
  const scaleH = ch / h;
  const scale = Math.max(scaleW, scaleH); // 取大值
  return scale;
}

和 contain 相反,算出将图片的宽高分别缩放为容器宽高需要的比例之后,要取其中的大值。

因为我们是要尽可能填充容器,另一个边如果超出了容器范围,就将其截断。

3lSHO0

fill

fill,图片完全填充容器,不要求保持宽高比,可以对图片进行拉伸。

非常少用,因为会将图片拉伸,导致图片非常难看。

实现上,只要直接将图片的宽高设置为容器的宽高即可,不需要用到什么计算。

1
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

furVJo

none

none,图片保持原本大小、不进行缩放地显示。

1
ctx.drawImage(img, x, y, img.width, img.height);

3jXl1A

scale-down

scale-down,应用 contain 和 none 中图片尺寸较小的。

这么做是希望尺寸小于容器的图片能保持原比例,而不是被放大导致失真。

1
2
3
4
5
6
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 的方式。

Afzwzl

总结

西瓜哥我总结一下这 5 种图片填充容器的方式:

  1. contain:刚好完整放入容器,不多也不少;
  2. cover:充满容器,但尺寸尽量小;
  3. fill:不保持宽高比,直接拉伸填充容器;
  4. none:保持图片最初的模样;
  5. scale-down:如果可以不放大就能放入容器,直接放入(none);如果不能,就放大点,让其刚好放入容器(contain)

本文首发于我的公众号:前端西瓜哥