渐进式图像加载 - 模糊占位符图像(如 Unsplash)

Unsplash 是我最喜欢的图片网站,不仅因为它有大量免费图片,还因为它的用户体验非常好。Unsplash 开始使用 BlurHash 来提供 渐进式图片加载 体验已经有一段时间了。

BlurHash added to photo objects

概念

渐进式图像加载

渐进式图像加载 可带来更好的用户体验。但什么是渐进式图像加载?

无需等待最终图像渲染完成,先使用低质量图像作为占位符 (LQIP),加载完成后再切换到原始图像。

发生了什么?

  1. 使用特定 aspect-ratio 渲染元素(例如 div),通常与原始图像相同
  2. 先渲染 低质量图像
  3. 当此元素 在视口中时,触发加载原始图像
  4. 原始图像加载完成后,在低质量图像上渲染原始图像,然后在背景上 隐藏/删除低质量图像

以下是一个例子:

占位图 vs 原始图

⬅️ 这个 8×6 像素的图像可以放大多次(例如,在我的屏幕上为 865×649 像素),而不会增加明显的噪点。

Unsplash 是怎么做的

  1. 使用 XHR 获取列表数据
  2. 从列表项的 blur_hash 字段解码 BlurHash
  3. 从使用解码的 BlurHash 数据绘制的画布生成 8 x 8 像素 image/bmp 图像,并将其渲染为图像占位符
  4. 在加载时渲染原始图像

为什么用 BMP 呢?

请查看 图片大小对比.

此外,渲染小尺寸图像比绘制画布消耗更少的 CPU 和内存。

如何生成占位图?

Photoshop 是处理滤镜效果(例如高斯模糊)的绝佳工具,但对于大多数用户来说太复杂了。

Photoshop

因此我创建了一个 Web 应用程序 (Image Blurrer) 来生成模糊的占位符图像。支持 4 种类型的输出图像:

  1. StackBlur
  2. Gaussian Blur
  3. BlurHash
  4. CSS Gradient

对比

图像质量

使用我的 Image Blurrer,在使用 StackBlur 生成模糊图像时,可以自定义模糊半径。在我的个人使用和测试中,StackBlur 是我的最爱,因为它的模糊半径灵活。

画布大小模糊半径
32 px2 px
StackBlur
Gaussian Blur
BlurHash
CSS Gradient

BlurHash 在大多数情况下是一个不错的选择(可以从 Unsplash 的经验中证明),但是在某些情况下它会将原始图像的暗区渲染得太暗。

例如:

原始图片BlurHash
https://r2-assets-cn.thelynan.com/uPic/P1010249.jpgUcF~mn?b00D$~q-:IUM{?c%3M{RjWBWBt7xu

图片尺寸对比

测试图片: https://r2-assets-cn.thelynan.com/uPic/progress-image-loading-test-img.jpg

我在测试结果中突出显示了最小尺寸。在我的测试中,当画布宽度低于 10px 时,image/bmp 最小,然后在 12px - 22px 之间,image/png 占据主导地位,之后 image/jpeg 最小。

width(px)jpegpngbmp
4816 B132 B90 B
6828 B168 B135 B
8825 B222 B198 B
10846 B279 B279 B
12867 B366 B378 B
14885 B432 B495 B
16882 B546 B630 B
18936 B618 B783 B
20954 B768 B954 B
22942 B855 B1.1 KB
241.0 KB1.0 KB1.3 KB
261.0 KB1.1 KB1.5 KB
281.1 KB1.3 KB1.8 KB
301.1 KB1.4 KB2.0 KB
321.1 KB1.6 KB2.3 KB

实际上,大多数浏览器不支持通过 canvas.toDataURL 直接生成 image/bmp,如果指定的类型不受支持,则将使用 image/png 格式。

解决方案:

  1. canvs-to-bmp
  2. jimp 我的选择,因为我已经用它来生成 CSS 渐变,而且在我的实践中,jimp 生成尺寸较小的 bmp 图像文件。
jimp getBase64Async
1
2
3
4
const img = await Jimp.read("./path/to/image.jpg");
img.getBase64Async("image/bmp").then((res) => {
resolve(res);
});

感谢

  1. StackBlur
  2. Gaussian Blur
  3. BlurHash
  4. CSS Gradient
  5. jimp