现代图片性能优化及体验优化指南
图片资源,在我们的业务中可谓是占据了非常大头的一环,尤其是其对带宽的消耗是十分巨大的。
对图片的性能优化及体验优化在今天就显得尤为重要。本文,就将从各个方面阐述,在各种新特性满头飞的今天,我们可以如何尽可能的对我们的图片资源,进行性能优化及体验优化。
图片类型的选取及 Picture 标签的使用
首先,从图片的类型上而言,除了常见的 PNG-8/PNG-24,JPEG,GIF 之外,我们更多的关注另外几个较新的图片格式:
- WebP
- JPEG XL
- AVIF
首先,通过一张表格,快速过一下这几个图片,我们将从图片类型、透明通道、动画、编解码性能、压缩算法、颜色支持、内存占用、兼容性方面,对比它们:
图片类型 | Alpha 通道 | 动画 | 编解码性能 | 压缩算法 | 颜色支持 | 内存占用 | 兼容性 |
---|---|---|---|---|---|---|---|
GIF | 支持 | 支持 | 较高 | 无损压缩 | 索引色(256) | 基本一致 | ALL |
PNG-8/PNG-24 | 支持 | 不支持 | 较高 | 无损压缩 | 索引色(256)\直接色 | 基本一致 | ALL |
JPEG | 不支持 | 不支持 | 较高 | 有损压缩 | 直接色 | 基本一致 | ALL |
WebP | 支持 | 支持 | 编解码性能差(低配设备更为显著) | 有损压缩\无损压缩 | 直接色 | 基本一致 | 高版本 Chrome\Opera\Android |
JPEG XL | 支持 | 支持 | 渐进式解码 | 有损压缩\无损压缩 | 直接色 | 基本一致 | 部分高版本 Chrome\Opera\Firefox\Edge |
AVIF | 支持 | 支持 | 编解码性能一般 | 有损压缩\无损压缩 | 直接色 | 基本一致 | 高版本 Chrome\Opera\Android\Edge |
首先,了解了解上述的一些参数含义:
- Alpha 通道:图片是否支持透明的特性
当然,需要指出的是,Alpha 没有透明度的意思,不代表透明度。opacity 和 transparency 才和透明度有关,前者是不透明度,后者是透明度。比如 css 中的「opacity: 0.5」就是设定元素有 50% 的不透明度。后来 Alvy Ray Smith 提出每个像素再增加一个 Alpha 通道,取值为0到1,用来储存这个像素是否对图片有「贡献」,0代表透明、1代表不透明。也就是说,「Alpha 通道」储存一个值,其外在表现是「透明度」,Alpha 和透明度没啥关系
- 动画:很好理解,图片是否支持多帧率动态图片,类似于 GIF
- 编解码性能:图像的解码与编码。这个很关键,很多人对待图片容易忽视图片的编解码性能,解码图像主要从图像文件中读出图像数据,而编码则是将图像数据写入图像文件。解码与编码的过程正好相反。而这两者的性能耗时会影响我们页面的的展示性能。
- 压缩算法:该图片格式是否支持压缩,支持的话,图片的压缩又会分为无损压缩与有损压缩
有损压缩算法是一种数据压缩方法,经过此方法压缩、解压的数据会与原始数据不同但是非常接近。原理是借由将次要的信息数据舍弃,牺牲一些质量来减少数据量、提高压缩比
无损压缩指数据经过压缩后,信息不受损失,还能完全恢复到压缩前的原样。无损压缩通常用于严格要求“经过压缩、解压缩的数据必须与原始数据一致”的场合。
- 颜色支持:会分为索引色与直接色,在过去,为了节省存储空间,并非所有图片都能支持所有颜色值,因此存在索引色这种优化方式。
索引颜色是一种以有限的方式管理数字图像颜色的技术,以节省计算机内存和文件存储,同时加速显示刷新和文件传输。即用一个数字来代表(索引)一种颜色,在存储图片的时候,存储一个数字的组合,同时存储数字到图片颜色的映射。这种方式只能存储有限种颜色。索引色常见有1位(即黑白),8位(即灰阶/256色),16位(即高彩),24位(即真彩),30/36/48位(即全彩)。
直接色使用四个数字来代表一种颜色,这四个数字分别代表这个颜色中红色、绿色、蓝色以及透明度(即 RGBA)。现在流行的显示设备可以在这四个维度分别支持256种变化,所以直接色可以表示2的32次方种颜色。
- 内存占用:图片对内存资源的占用
- 兼容性:影响图片格式能否大规模推广的核心要素之一
WebP vs JPEG XL vs AVIF: JPEG 替代之战
因为传统的 PNG-8/PNG-24,JPEG,GIF 各自或多或少都存在一些问题,近些年来它们的替代方案之争也愈演愈烈,核心领跑者可能是 WebP、JPEG XL、AVIF。
再简单了解了解它们:
WebP
WebP 最初由 Google 在 2010 年 9 月发布,其特性总结如下:
- 可以同时提供无损/有损压缩(像 JPEG 一样)和支持透明度(像 PNG 一样)的图片文件格式
- 支持动画效果(像 GIF 一样)
- WebP 主要优势在于有损编码,其无损编码的性能和压缩比表现一般
- WebP 的缺点在于其编解码性能不是特别理想
- 在兼容性方面,除了 IE,基本已经得到了全系列浏览器支持
对于复杂的图像(比如照片)来说,WebP 无损编码表现并不好,但有损编码表现却非常棒。相近质量的图片解码速度 WebP 相距 JPEG 也已经相差不大了,而文件压缩比却能提升不少。
下图是我之前还在 TX 的时候做的一个测试对比:
加载同样张数的 JPEG 与 WebP 的耗时对比:
对于 WebP 图片格式,简单做个总结:
- 目前 WebP 与 JPEG 相比较,据资料考证,编码速度慢 10 倍,解码速度慢 1.5 倍
- WebP 虽然会增加额外的解码时间,但由于大幅减少了文件体积,缩短了加载的时间,大页面图片量较多的场景下,页面的渲染速度是有较大加快的
- 目前而言,是 WebP、JPEG XL、AVIF 三者中兼容性最好的
截止至(2023-02-05)的兼容性图:
JPEG XL
JPEG XL 由联合图像专家组(开发原始 JPEG 标准的同一组织)于 2021 年发布,旨在成为传统 JPEG 的长期替代品。作为一种免版税的开源标准,JPEG XL 的创建者希望其格式的开放性能够吸引网络开发人员采用该标准,该格式的扩展名为 .jxl
,JXL 核心比特流于 2021 年 1 月冻结,文件格式于 2021 年 4 月定稿。:
JPEG XL 中的 X 指 2000 年以来的多个 JPEG 标准的名称:JPEG XT、JPEG XR、JPEG XS,而 L 表示 'long-term',表示“长期”。创建这种格式是为替换旧的JPEG文件格式,并使用足够长的时间。
其主要特点有:
- 与传统图像格式(例如JPEG、GIF和PNG)相比,有着更佳的效率与更丰富的功能
- 全面支持广色域和 HDR,支持 Alpha 通道,支持多帧(也就是动画支持)
- 有损压缩时:相同的视觉质量,比 JPEG 小约 60%。
- 无损压缩时:比 PNG 减小约 35%(对于 HDR,减小 50%)。
- 支持无损 JPEG 转码,减小约 20% 文件大小。
- 渐进式解码,专为支持不同显示分辨率的响应式加载
- 开源免费:具有使用三条款版BSD许可证的开源参考实现的免版税格式
看看同一张图片,相同质量下的大小表现:
JPEG XL 是目前而言,最有可能全面替代传统图片格式(Gif、PNG、JPEG)的下一代标准,当然,在今天,需要看看其兼容性:
好吧,目前的兼容有点离谱。Chrome 从 91 版本开始已经实验室性质支持了 .jxl
格式的图片,需要通过 --enable-features=JXL
配置开启,遗憾的是,从 Chrome 110 开始,Chrome 又不再支持 JPEG XL 。
有趣的是,Chrome 从 110 版本开始中弃用了对 JPEG-XL 的支持,谷歌的回答是,人们对 JPEG-XL 没有足够的兴趣,并且与现有格式相比也没有足够的优势。谷歌之前一直对 JPEG 的支持都是实验性的性质的,他们认为 JPEG XL 缺乏生态系统支持,并且该格式没有足够多的好处(相对 WebP 和 AVIF)。也就是说,目前而言,Chrome 对 WebP 和 AVIF 等替代格式更感兴趣。
AVIF
最后,我们再来看看 AVIF 格式图片。
AVIF 是由开放媒体联盟 (AOM) 开发并于 2019 年发布的另一种最新图像格式。该格式基于 AV1 视频编解码器,源自视频帧。其特点如下:
- 同样的,与传统图像格式(例如JPEG、GIF和PNG)相比,有着更佳的效率与更丰富的功能
- 支持 Alpha 通道,支持动态图像和动画
- 支持有损、无损压缩。AVIF 文件在低保真有损图像压缩方面表现出色(比 JPEG XL 更优)。压缩的 AVIF 图像保留了很高的图片质量,避免了恼人的压缩伪影等问题
- 相对而言,AVIF 的解码和编码速度不如 JPEG XL,它不支持渐进式渲染
- 最后,再看看兼容性,目前(2023-02-05)它的兼容性介于 WebP 与 JPEG XL 之间
看看 CaniUse 的数据:
下图是 WebP vs JPEG XL vs AVIF 三者在图片解码上的性能表现:
从图中可以看到,对于解码性能的对比,结果居然是 WebP > AVIF > JPEG XL 。JPEG XL 的编解码性能并没有其描述的那么强大。
图片格式总结
总结一下,WebP、AVIF 和 JPEG XL 都是浏览器不广泛支持的新型图像格式。虽然 WebP、AVIF 已经存在很长时间,但到今天,影响它们大规模使用的依旧是兼容问题。它们各自有各自的特点与优势,谁能胜出仍未知晓。
虽然 AVIF、JPEG XL 等新型图片格式未得到任何浏览器的完全支持,但是在新版本的 Chrome、Firefox 和 Edge Chromium,可以使用配置标志启用对应图像格式,配合 HTML 的 Picture
标签,我们还是可以一定程度上对我们的图片进行格式选择上的优化的。
这,就可以引出我们要说的第二部分 -- HTML Picture 标签的使用。
Picture 元素的使用
HTML5 规范新增了 Picture Element。那么 <picture>
元素的作用是什么呢?
<picture>
元素通过包含零或多个 <source>
元素和一个 <img>
元素来为不同的显示/设备场景提供图像版本。浏览器会选择最匹配的子 <source>
元素,如果没有匹配的,就选择 <img>
元素的 src 属性中的 URL。然后,所选图像呈现在 <img>
元素占据的空间中。
什么意思呢?怎么使用 <picture>
元素呢?
假设,没有 <picture>
,只有 <img>
元素,我们想尽可能在支持一些现代图片格式的浏览器上使用类似于上述我们提到的 WebP、AVIF 和 JPEG XL 等图片格式,而不支持的浏览器回退使用常规的 JPEG、PNG 等。没错,就是一种渐进增强的思想,该怎么办呢?
只能是 JavaScript 去写对应的逻辑,通过 JS 脚本进行特性查询,动态赋值给 <img>
的 src。
而有了 <picture>
后,浏览器将原生支持上述的一些列操作,我们来看看对应的语法:
<picture>
<!-- 可能是一些对兼容性有要求的,但是性能表现更好的现代图片格式-->
<source src="image.avif" type="image/avif">
<source src="image.jxl" type="image/jxl">
<source src="image.webp" type="image/webp">
<!-- 最终的兜底方案-->
<img src="image.jpg" type="image/jpeg">
</picture>
上述代码的含义是:
- 第 1 个
source
元素指向新 AVIF 格式的图像。如果浏览器支持 AVIF 图像,那么它会选择该图像文件。否则,它将移动到下一个source
元素。 - 第 2个
source
元素指向新 JPEG XL 格式的图像。如果浏览器支持 JPEG XL 图像,那么它会选择该图像文件。否则,它将移动到下一个source
元素。 - 第 3 个
source
元素指向一张WebP 格式的图像。如果浏览器能够渲染 WebP 图像,它将使用该图像文件。 - 否则浏览器将回退到使用
img
元素 src 属性中的图像文件。img 元素指向的是 JPEG 格式的图片,它是最终的兜底方案。
这意味着现在我们可以在不牺牲向后兼容性的情况下开始使用新的图像格式。
简而言之,<picture>
元素的作用:
- 通过
<source>
给出一系列对兼容性有所要求的现代图片格式选项 - 通过
<img>
给出兜底的高兼容性图片格式选项 - 浏览器通过对给出的图片格式做特性检测,要决定加载哪个 URL,user agent 检查每个
<source>
的 srcset、media 和 type 属性,来选择最匹配页面当前布局、显示设备特征等的兼容图像。 - 最终,所选图像呈现在
<img>
元素占据的空间中
模块总结
总结一下,本文对常见的图片格式以及最新的几种未被大规模兼容的图片格式进行的对比,它们分别是:
- PNG-8/PNG-24
- JPEG
- GIF
- WebP
- JPEG XL
- AVIF
其后,着重介绍了 3 种现代图片格式:WebP、JPEG XL、AVIF。相对于 JPEG 等传统格式,它们在色彩表现、动画支持、是否支持无损有损压缩、压损比率、编解码性能上有着更进一步的提升,正在成为下一阶段 Web 图像的标准。
最后,介绍了 <picture>
元素,借助它,我们能更好的实现图片的渐进增强。
适配不同的屏幕尺寸及 DPR
下一个模块,我们来看看图片资源如何更好的适配不同的屏幕尺寸。
这里首先会涉及一个预备知识,屏幕的 DPR 值,那么,什么是 DPR 呢?要了解 DPR,又需要知道什么是设备独立像素 以及 物理像素。
设备独立像素
以 iPhone6/7/8为例,这里我们打开 Chrome 开发者工具:
这里的 375 * 667
表示的是什么呢,表示的是设备独立像素(DIP),也可以理解为 CSS 像素,也称为逻辑像素:
设备独立像素 = CSS 像素 = 逻辑像素
如何记忆呢?这里使用 CSS 像素来记忆,也就是说。我们设定一个宽度为 375px 的 div,刚好可以充满这个设备的一行,配合高度 667px ,则 div 的大小刚好可以充满整个屏幕。
物理像素
OK,那么,什么又是物理像素呢。我们到电商网站购买手机,都会看一看手机的参数,以 JD 上的 iPhone7 为例:
可以看到,iPhone7 的分辨率是 1334 x 750
,这里描述的就是屏幕实际的物理像素。
物理像素,又称为设备像素。显示屏是由一个个物理像素点组成的,1334 x 750
表示手机分别在垂直和水平上所具有的像素点数。通过控制每个像素点的颜色,就可以使屏幕显示出不同的图像,屏幕从工厂出来那天起,它上面的物理像素点就固定不变了,单位为pt。
设备像素 = 物理像素
DPR(Device Pixel Ratio) 设备像素比
OK,有了上面两个概念,就可以顺理成章引出下一个概念。DPR(Device Pixel Ratio) 设备像素比,这个与我们通常说的视网膜屏(多倍屏,Retina屏)有关。
设备像素比描述的是未缩放状态下,物理像素和设备独立像素的初始比例关系。
简单的计算公式:
DPR = 物理像素 / 设备独立像素
我们套用一下上面 iPhone7 的数据(取设备的物理像素宽度与设备独立像素宽度进行计算):
iPhone7’s DPR = iPhone7’s 物理像素宽度 / iPhone7's 设备独立像素宽度 = 2
750 / 375 = 2
或者是 1334 / 667 = 2
可以得到 iPhone7 的 dpr 为 2。也就是我们常说的视网膜屏幕。
视网膜(Retina)屏幕是苹果公司"发明"的一个营销术语。 苹果公司将 dpr > 1
的屏幕称为视网膜屏幕。
在视网膜屏幕中,以 dpr = 2 为例,把 4(2x2) 个像素当 1 个像素使用,这样让屏幕看起来更精致,但是元素的大小本身却不会改变:
OK,我们再来看看 iPhone XS Max:
它的物理像素如上图是 2688 x 1242
,
它的 CSS 像素是 896 x 414
,很容易得出 iPhone XS Max 的 dpr 为 3。
为不同 DPR 屏幕,提供恰当的图片
那么,DPR 和图片适配有什么关系呢?
举个例子,同样的 CSS 像素大小下,屏幕如果有不同 DPR,同样大小的图片渲染出来的效果不尽相同。
我们以 dpr = 3
的手机为例子,在 300 x 389
CSS 像素大小的范围内,渲染 1倍/2倍/3倍 图的效果如下:
实际图片所占的物理像素为 900 x 1167。
可以看到,在高 DPR 设备下提供只有 CSS 像素大小的图片,是非常模糊的。
因此,为了在不同的 DPR 屏幕下,让图片看起来都不失真,我们需要为不同 DPR 的图片,提供不同大小的图片。
那么,有哪些可行的解决方案呢?
方案一:无脑多倍图
假设,在移动端假设我们需要一张 CSS 像素为 300 x 200
的图像,考虑到现在已经有了 dpr = 3 的设备,那么要保证图片在 dpr = 3 的设备下也正常高清展示,我们最大可能需要一张 900 x 600
的原图。
这样,不管设备的 dpr 是否为 3,我们统一都使用 3 倍图。这样即使在 dpr = 1,dpr = 2 的设备上,也能非常好的展示图片。
当然这样并不可取,会造成大量带宽的浪费。
现代浏览器,提供了更好的方式,让我们能够根据设备 dpr 的不同,提供不同尺寸的图片。
方案二:媒体查询
方案二,我们可以考虑使用媒体查询。到今天,我们可以通过相应的媒体查询,得知当前的设备的 DPR 值,这样,我们就可以在对应的媒体查询中,使用对应的图片。
像是这样:
#id {
background: url(xxx@2x.png)
}
@media (device-pixel-ratio: 2) {
#id {
background: url(xxx@2x.png)
}
}
@media (device-pixel-ratio: 3) {
#id {
background: url(xxx@3x.png)
}
}
这个方案的缺点在于:
- 要写的代码可能太多了,而且,可能存在一些介于 12,23 之间的 DPR 值,不好穷举出所有场景
- 需要注意语法需要的兼容性,需要添加前缀,譬如
-webkit-min-device-pixel-ratio
,当然这个可以由autoprefixer
辅助解决
方案三:CSS 配合 image-set 语法
image-set
属于 CSS background 中的一种语法,image-set()
函数为设备提供最合适的图像分辨率,它提供一组图像选项,每个选项都有一个相关的 DPR 声明,浏览器将从中选择最适合设备的图像进行设置。
什么意思呢,来看看代码:
.img {
/* 不支持 image-set 的浏览器*/
background-image: url('../photo@2x.png');
/* 支持 image-set 的浏览器*/
background-image: image-set(
url('./photo@2x.png') 2x,
url('./photo@3x.png') 3x
);
}
这样一看,作用应该很清晰了。对于支持 image-set
语法的浏览器:
- 如果其设备对应的 DPR 为 2,会选取这条
url('./photo@2x.png') 2x
记录,也就是最终生效的 URL 是'./photo@2x.png'
; - 如果其设备对应的 DPR 为 3,会选取这条
url('./photo@3x.png') 3x
记录,也就是最终生效的 URL 是'./photo@3x.png'
;
其中的 2x
,3x
就是用于匹配 DRP的。
使用 image-set
的一些痛点与媒体查询方案类似。代码量与兼容性语法,而且难以匹配所有情况。
方案四:srcset 配合 1x 2x 像素密度描述符
简单来说,srcset 可以根据不同的 dpr 拉取对应尺寸的图片:
<div class='illustration'>
<img src='illustration-small.png'
srcset='images/illustration-small.png 1x,
images/illustration-big.png 2x'
>
</div>
上面 srcset
里的 1x,2x 表示 像素密度描述符,表示
- 当屏幕的 dpr = 1 时,使用
images/illustration-small.png
这张图 - 当屏幕的 dpr = 2 时,使用
images/illustration-big.png
这张图 - 如果不支持
srcset
语法,src='illustration-small.png'
将会是最终的兜底方案
方案五:srcset 属性配合 sizes 属性 w 宽度描述符
上面 1x,2x 的写法比较容易接受易于理解。
但是,上述 3 种方案都存在统一的问题,只考虑了 DPR,但是忽略了响应性布局的复杂性与屏幕的多样性。
因此,规范还推出了一种方案 -- srcset 属性配合 sizes 属性 w 宽度描述符。
srcset
属性还有一个 w 宽度描述符,配合 sizes
属性一起使用,可以覆盖更多的面。
sizes
属性怎么理解呢?它定义图像元素在不同的视口宽度时,可能的大小值。
以下面这段代码为例子:
<img
sizes = “(min-width: 600px) 600px, 300px"
src = "photo.png"
srcset = “photo@1x.png 300w,
photo@2x.png 600w,
photo@3x.png 1200w,
>
解析一下:
sizes = “(min-width: 600px) 600px, 300px"
的意思是:
- 如果屏幕当前的 CSS 像素宽度大于或者等于 600px,则图片的 CSS 宽度为 600px
- 反之,则图片的 CSS 宽度为 300px
也就是 sizes 属性声明了在不同宽度下图片的 CSS 宽度表现。这里可以理解为,大屏幕下图片宽度为 600px,小屏幕下图片宽度为 300px。
需要注意的是,这里大屏、小屏下图片具体的宽度表现,还是需要借助媒体查询代码,经由 CSS 实现的
srcset = “photo@1x.png 300w, photo@2x.png 600w, photo@3x.png 1200w
里面的 300w,600w,900w 叫宽度描述符。
那么,怎么确定当前场景会选取哪张图片呢?
当前屏幕 dpr = 2 ,CSS 宽度为 375px。
当前屏幕 CSS 宽度为 375px,则图片 CSS 宽度为 300px。分别用上述 3 个宽度描述符的数值除以 300。
- 300 / 300 = 1
- 600 / 300 = 2
- 1200 / 300 = 4
上面计算得到的 1、 2、 4 即是算出的有效的像素密度,换算成和 x 描述符等价的值 。这里 600w 算出的 2 即满足 dpr = 2 的情况,选择此张图。
当前屏幕 dpr = 3 ,CSS 宽度为 414px。
当前屏幕 CSS 宽度为 414px,则图片 CSS 宽度仍为 300px。再计算一次:
- 300 / 300 = 1
- 600 / 300 = 2
- 1200 / 300 = 4
因为 dpr = 3,2 已经不满足了,则此时会选择 1200w 这张图。
当前屏幕 dpr = 1 ,CSS 宽度为 1920px。
当前屏幕 CSS 宽度为 1920px,则图片 CSS 宽度变为了 600px。再计算一次:
- 300 / 600 = .5
- 600 / 600 = 1
- 1200 / 600 = 2
因为 dpr = 1,所以此时会选择 600w 对应的图片。
具体的可以试下这个 Demo:CodePen Demo -- srcset属性配合w宽度描述符配合sizes属性
此方案的意义在于考虑到了响应性布局的复杂性与屏幕的多样性,利用上述规则,可以一次适配 PC 端大屏幕和移动端高清屏,一箭多雕。
嗯,总结一下,在实现响应式图像时,我们同时使用 srcset
和 sizes
属性。它们的作用是:
srcset
:定义多个不同宽度的图像源,让浏览器在 HTML 解析期间选择最合适的图像源sizes
:定义图像元素在不同的视口宽度时,可能的大小值
有了这些属性后,浏览器就会根据 srcset/size 来创建一个分辨率切换器的响应式图片,可以在不同的分辨率的情况下,提供相同尺寸的图像,或者在不同的视图大小的情况下,提供不同尺寸大小的图像。
模块总结
本章节一共列举了 5 种实现响应式图片,适配不同屏幕大小,不同 DPR 的方式,它们分别是:
- 无脑多倍图的方式
- DRP 媒体查询
- CSS Background 中的使用
image-set
- srcset 配合 1x 2x 像素密度描述符
- srcset 属性配合 sizes 属性 w 宽度描述符
合理使用它们,可以有效的为不同屏幕,提供最为恰当的图片资源,在保证用户体验的同时,尽可能节省带宽。
它们各有优缺点,可以根据自己实际的业务场景,选取合适相对成本最低的方案,并且适当的配合 Autoprefixer 以及一些 PostCSS 等工具,简化代码量。
图片的宽高比、裁剪与缩放
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
的取值有 fill
、none
、contain
、cover
,与 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
: