图片是 BBKi.ng 的主要内容之一,友好地展示图片是必须考虑的问题。
先写下第一行代码:
<img src="my-awesome-picture.jpg" />
这样就完成了图片展示。但是这种极简的做法存在一些问题,还谈不上友好。比如:
- 布局偏移问题。我们没有为
img
标签设定尺寸,导致图片加载期间,浏览器没有在文档中为其分配正确的空间。加载完成后,图片突然撑开。 - 性能问题。没有惰性渲染、惰性加载,多图页面会有潜在性能问题。
- 逐行渲染问题。标准 JPEG 格式压缩的图片,下载时基线 JPEG 算法会逐行渲染图片,影响体验。
布局偏移
对于问题 1. 我们为图片设置好宽高即可:
<style>
img {
max-width: 100%;
height: auto;
}
</style>
<img height="853" width="1280" … />
性能相关
content-visibility: auto
该 CSS 属性会告知浏览器,在图片接近屏幕前,先不要为其布局。浏览器也不会去解码用户暂时看不见的图片,节省 CPU。
loading="lazy"
该属性会告知浏览器,在图片接近屏幕前,先不要去请求图片资源。
decoding="async"
该属性会给浏览器脱离主线程解码图片的权限,避免用来解码图片的 CPU 时间影响用户。
呈现相关
渐进加载
直观体验:
上边两个例子在加载大图时,都用到了低质量图片占位(Low Quality Image Placeholder, LQIP)。在大图加载时,展示模糊的低质量图片,在加载低质量图片时,展示背景色。图片加载成功时,配合 transition 效果,整个过程显得顺滑流畅。 José M. Pérez 在 2015 年写了一篇文章详细分析了 Medium 图片渐进加载细节。
了解基本思路后,实现方式可以有很多种。其中关键,需要准备高清图对应的低质量图片,以及对低质量图片进行模糊处理。
低质量图片
BBKi.ng 所有图片均存放在阿里云 OSS 中。OSS 数据处理下的图片处理功能可以创建样式。不同样式对应不同的图片处理细节,如图片格式转换、图片质量、缩略等。我们可以新建名为 LQIP
的样式,将原图压缩为低质量图片。假设上传原图后得到的地址为:url/to/my-awesome-picture.jpg
, 那么通过地址 url/to/my-awesome-picture.jpg?x-oss-process=style/LQIP
即能获取低质量图片。
模糊效果
实现图片模糊有很多种方式。比如
img {
filter: blur(2px)
}
.cover {
backdrop-filter: blur(2px);
}
以上几种方式都各有优缺点。CSS 滤镜优点是写法比较简单,也无需引入新的元素。缺点是,边缘也会模糊掉。可以增加一个包裹元素,设置 overflow: hidden
解决,但是模糊半径较大时,图片边缘的模糊效果始终不是很理想(效果对比);用 CSS 背景滤镜可以得到锐利清晰的边缘,同时图片内容区域模糊效果也很好,缺点是效果是应用在元素背后的区域,因此我们需要引入额外的元素,同时该样式会影响图层合成时间、默认不兼容火狐浏览器;Blurhash 方法的优点是模糊图片只需要用简洁哈希字符串表示,节省空间。缺点是编码解码过程稍显繁琐。同时客户端解码大尺寸图片时也存在性能问题。
综合考虑后,最终使用背景滤镜对图片进行模糊处理。
示例

参考链接
- https://web.dev/optimize-cls/
- https://medium.com/hd-pro/jpeg-formats-progressive-vs-baseline-73b3938c2339
- https://www.industrialempathy.com/posts/image-optimizations/
- https://www.guypo.com/introducing-lqip-low-quality-image-placeholders
- https://jmperezperez.com/medium-image-progressive-loading-placeholder/
- https://github.com/vercel/next.js/blob/canary/packages/next/client/image.tsx