现代图片性能优化及体验优化指南 - 缩放精细化展示及避免布局偏移、拉伸

11,262 阅读7分钟

本文是系列第三篇。系列文章:

  1. 现代图片性能优化及体验优化指南 - 图片类型及 Picture 标签的使用
  2. 现代图片性能优化及体验优化指南 - 响应式图片方案

图片资源,在我们的业务中可谓是占据了非常大头的一环,尤其是其对带宽的消耗是十分巨大的。

对图片的性能优化及体验优化在今天就显得尤为重要。本文,就将从各个方面阐述,在各种新特性满头飞的今天,我们可以如何尽可能的对我们的图片资源,进行性能优化及体验优化。

图片的宽高比、裁剪与缩放

OK,下面进入到我们的第三个模块,图片的宽高比、裁剪与缩放。我们会介绍 4 个新的特性:

  • aspect-ratio
  • object-fit
  • object-position
  • image-rendering

使用 aspect-ratio 避免布局偏移

很多时候,只能使用固定尺寸大小的图片,我们的布局可能是这样:

对应的布局:

<ul class="g-container">
    <li>
        <img src="http://placehold.it/150x100">
        <p>图片描述</p>
    </li>
</ul>
ul li img {
    width: 150px;
}

当然,万一假设后端接口出现一张非正常大小的图片,上述不加保护的布局就会出问题:

所以对于图片,我们总是建议同时写上高和宽,避免因为图片尺寸错误带来的布局问题:

ul li img {
    width: 150px;
    height: 100px;
}

同时,给 <img> 标签同时写上高宽,可以在图片未加载之前提前占住位置,避免图片从未加载状态到渲染完成状态高宽变化引起的重排问题。

当然,到今天,我们还可以使用 aspect-ratio 设定图片的高宽比。

aspect-ratio CSS 属性为容器规定了一个期待的宽高比,这个宽高比可以用来计算自动尺寸以及为其他布局函数服务。

像是上面的代码,我们就可以替换成:

ul li img {
    width: 150px;
    aspect-ratio: 3 / 2;
}

当然,有的时候,我们的布局是响应式动态在变化的,容器的宽度也是不确定的,因此,有了 aspect-ratio 之后,我们的写法就可以更佳的舒服。

ul li img {
    width: 100%;
    aspect-ratio: 3 / 2;
}

这里,容器基于 Flex 弹性布局或者响应式布局,其宽度是不固定的,但是图片的宽高比是固定的,使用 aspect-ratio: 3 / 2 就能非常好的适配这种情况。

我们借助了 aspect-ratio 这个 CSS 中较新的属性来始终自动获得正确的宽高比,无论其父元素的宽度如何变化。

当然,aspect-ratio 不仅仅只是能运用在这里,在 aspect-ratio 出现之前,我们只能通过一些其它的 Hack 方式,譬如设置 padding-top 等方式模拟固定的宽高比。在 aspect-ratio 之后,我们终于有了设定容器固定宽高比的能力。

object-fit 避免图片拉伸

当然,限制高宽也会出现问题,譬如图片被拉伸了,非常的难看:

这个时候,我们可以借助 object-fit,它能够指定可替换元素的内容(也就是图片)该如何适应它的父容器的高宽。

ul li img {
    width: 150px;
    aspect-ratio: 3 / 2;
    object-fit: cover;
}

利用 object-fit: cover,使图片内容在保持其宽高比的同时填充元素的整个内容框。

object-fit 的取值有 fillnonecontaincover,与 background-size 类似,可以类比记忆。

也可以看看这张图,很易于理解:

object-fit 还有一个配套属性 object-position,它可以控制图片在其内容框中的位置。(类似于 background-position),默认是 object-position: 50% 50%,如果你不希望图片居中展示,可以使用它去改变图片实际展示的 position。

ul li img {
    width: 150px;
    aspect-ratio: 3 / 2;
    object-fit: cover;
    object-position: 50% 100%;
}

像是这样,object-position: 100% 50% 指明从底部开始展示图片。这里有一个很好的 Demo 可以帮助你理解 object-position

CodePen Demo -- Object position

使用 image-rendering 设置图片缩放算法

相对于上面几个新特性,image-rendering 会更为冷门。

很多时候,我们设置一个图片在页面上的展示大小为 200px x 200px,但是图片的原始尺寸可能是 800px x 800px,也可能是 50px x 50px

这个时候,我们就可以利用 image-rendering,设置图片在缩放状态下的展示算法。

image-rendering 在特定的场景下,能够起到奇效。

来看这样一个有意思的 DEMO,假设我们有这样一个原图效果,它是一个二维码,大小为 100px x 100px

如果我们将它放大,放到很大,明显,这个二维码会失真,像是这样:

OK,在这种放大失真的情况想,可以使用 image-rendering 改变图片缩放算法,这里我们试一下 image-rendering: pixelated

.img {
  image-rendering: pixelated;
}

效果变化,如下图所示:

可以看到,image-rendering: pixelated 处理过的图像,竟然变得如此清晰!

CodePen Demo -- QrCode Image-rendering demo

来看看 image-rendering 的几个取值:

  • image-rendering: auto:自 Gecko 1.9(Firefox 3.0)起,Gecko 使用双线性(bilinear)算法进行重新采样(高质量)。
  • image-rendering: smooth:使用能最大化图像客观观感的算法来缩放图像
  • image-rendering: high-quality:与 smooth 相同,但更倾向于高质量的缩放。
  • image-rendering: crisp-edges:必须使用可有效保留对比度和图像中的边缘的算法来对图像进行缩放,并且,该算法既不会平滑颜色,又不会在处理过程中为图像引入模糊。合适的算法包括最近邻居(nearest-neighbor)算法和其他非平滑缩放算法,比如 2×SaI 和 hqx-* 系列算法。此属性值适用于像素艺术作品,例如一些网页游戏中的图像。
  • image-rendering: pixelated:放大图像时,使用最近邻居算法,因此,图像看着像是由大块像素组成的。缩小图像时,算法与 auto 相同。

虽然规范定义了挺多值,但是实际上,现代浏览器基本暂时只支持:autopixelated、以及 -webkit-optimize-contrast(Chrome 下的 smooth)。

看描述都会挺懵逼的,实际使用的时候,最好每个都试一下验证一下效果。总结而言,image-rendering 的作用是在图像缩放时,提供不一样的渲染方式,让图片的展示形态更为多样化,或者说是尽可能的去减少图片的失真带来的信息损耗

我们再看一个 DEMO,原图如下(例子来源于 W3C 规范文档):

实际效果:

当然,看上去 pixelated 的效果挺好,这是由于这是一张偏向于矢量的图片,细节不多,对于高精度的人物图,就不太适用于 pixelated,容易把图片马赛克化。

真正规范希望的在放大后让图片尽可能不失真的 crisp-edges 效果,目前暂时没有得到浏览器的实现。后面可以期待一下。

CodePen Demo -- Image-rendering demo

总结一下

这一章,我们介绍了 4 个较新的 CSS 特性:

  • aspect-ratio:控制容器的宽高比,避免产生布局偏移及抖动
  • object-fit:设定内容应该如何适应到其使用高度和宽度确定的框,避免图片拉伸
  • object-position:基于 object-fit,设置图片实际展示的 position 范围
  • image-rendering:控制图片在缩放状态下的展示算法

合理利用它们,可以给用户在图片上以更好的体验。

当然,本文是现代图片性能优化及体验优化指南的第三篇,后续将给大家带来图片在:

  • 懒加载/异步图像解码方案
  • 可访问性以及图片资源的容错及错误处理

上的现代解决方案,感兴趣的可以提前关注。

最后

OK,本文到此结束,希望本文对你有所帮助 :)

更多精彩 CSS 技术文章汇总在我的 Github -- iCSS ,持续更新,欢迎点个 star 订阅收藏。

如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。