解读响应式网页中的响应式图片srcset+sizes处理方案
发布于 作者:苏南大叔 来源:程序如此灵动~
响应式图片的最好处理方案究竟是什么呢?动态更改图片的显示尺寸(width/height属性)么?还是要动态更改图片的地址?本文将要讨论其中的一个可能是最好的响应式图片方案:img的srcSet属性。

苏南大叔的“程序如此灵动”博客,记录苏南大叔和计算机代码的故事。事先说明的是,这个srcSet的概念表面上看很好理解,但是图片切换的理由和时机就非常难以理解了。测试环境:firefox@108.0.1,chrome@108.0.5359.125。
测试环境
首先要声明一下本文的测试环境,谷歌浏览器vs火狐浏览器。本文的测试代码是img的srcSet属性。期待的测试结果是:代码会根据浏览器的情况(视口宽度或者密度比)的变化而变化。
然而,谷歌浏览器的情况不容乐观。虽然会根据情况第一时间选择合适的图片地址,但是存在的问题是会缓存这个图片选择结果。在页面刷新前,无论相关的实际情况如何变化,都不会再次更改图片,也就是失去了响应。目前【稍稍有点改善】的方法就是:打开谷歌浏览器的开发者模式,然后点击network里面的“禁用缓存”选项(其他禁用缓存的方法无效)。

目前网络上的大部分攻略都表示:不知道选择某个图片的理由,是浏览器的自主行为。目前,使用市场上具有统治地位的谷歌浏览器就是无法解释,因为它的缓存结果太强大了,对srcSet的支持不好。所以,为了更好的理解srcSet的工作原理,本文的测试环境破天荒的改为了火狐浏览器。在这个浏览器下面,可以最好的演示和理解整个图片切换过程。
基本框架原则
响应式图片方案就是在img标签上面增加了特殊的srcSet和sizes来控制图片的显示的。例如:
<img src="0.png"
srcset="1.png 500w, 2.png 800w, 3.png 1200w"
sizes="(max-width:600px) 300px, 900px"
/>- 图片是很多张图片(理论上来说区别是像素密度清晰度的不同),只是对这些图片进行了动态切换。
srcSet属性,规定最基础的切换规则。尺寸的单位必须是w(别的尺寸不生效),理解为日常最常用的px。sizes属性可以不规定,主要的切换逻辑都存在于srcSet里面,它只是对srcSet属性的切换条件的再加工。而且,sizes内部使用px单位,而不使用w单位。
苏南大叔总结如下(经验总结,目前网络上无此描述):
srcset中的w为单位的数值 = 视口px宽度 * 硬件密度比下面通过不同的例子来对上述原则,进行理解。
根据视窗宽度选择
根据视窗的宽度的不同来切换图片的地址属性,这个是非常经典的操作。css通常也是利用media媒体查询视口宽度,进而修改页面样式来实现响应式设计的。
本文中的所有图片真实的尺寸都是100px宽。
img {
width: 100px;
}第一个例子【推荐】
<img srcset="1.png 550w, 2.png 850w, 3.png 1050w" src="0.png" />这段代码中,定义了三个图片地址,每个地址后面都(必须)使用w单位定义了一个宽度。
上述例子中,宽度实际上就是个切换图片的临界值,规定的区间是:
| 视窗宽度区间 | 响应式图片的实际取值 |
|---|---|
| (0,550/像素比] | 1.png |
| (550/像素比,850/像素比] | 2.png |
| (850/像素比,1050/像素比] | 3.png |
| (1050/像素比,正无穷大] | 3.png |
注意:取值区间一边是小括号,另外一边是中括号。像素比一般来说,电脑设备一般取值为1。而手机设备一般取值不为1。

第二个例子【败笔】【混乱】
第二个例子,在这个例子中,在srcSet后面增加了一个没有规定w的地址,导致全部逻辑被颠覆。
<img srcset="1.png 550w, 2.png 850w, 3.png 1050w, 4.png" src="0.png" />测试结果:
所有的w为单位的宽度值,
1)当密度比为1的时候,视口宽度只有确切的等于这个值(除以密度比1)的时候,才会显示对应的图片,而不是原来的区间。
2)当密度不为1的时候,表现为一个很奇怪的区间,真的很奇怪。
| 视窗宽度区间 | 响应式图片的实际取值 |
|---|---|
| (0,550/像素比] | 1.png |
| (550/像素比,850/像素比] | 2.png |
| (850/像素比,1050] | 3.png |
| (1050,正无穷大] | 4.png |
根据密度比进行选择
在picture的密度比切换方案测试中,失败了。但是在img的密度比测试方案中,是成功的。而且恒定解释为一个区间,并不会出现解释为某个确切值的情况。
<img srcset="1.png 1x, 2.png 2x, 3.png 3x" src="0.png" />实际的设备中,清晰度的衡量值:像素比并不恒定为整数。(用js去监测的话,表现为window.devicePixelRatio)
测试结果是:
| 像素比区间 | 取值 |
|---|---|
| (0,1] | 1.png |
| (1,2] | 2.png |
| (2,3] | 3.png |
| (3,正无穷大] | 3.png |
这里如果在最后增加一个没有定义像素比的图片地址的话,无效直接忽略即可。

增加sizes属性
sizes属性就是来搅局的,只规定srcSet属性就可以完美表述和切换了。但是,非要在这个srcSet属性的基础上再叠加一个sizes来再添变数。srcSet不是规定的被选择的标准么?size是规定选择的结果的,直接制定结果。
sizes可以直接规定值,以w为单位。实际上表述的值,和srcSet中的以w为单位的值,实际上是乘以密度比的关系。
sizes中的px值 * 密度比 = srcSet中的w值测试例子如下:
<img
srcset="1.png 550w, 2.png 850w, 3.png 1050w"
sizes="(max-width:800px) 200px,600px"
src="0.png"
/>这个表述的区间是:
| 实际像素比 | sizes规定:视口宽度px | 视同srcset中的值 | 落入srcset区间 | 最终结果 |
|---|---|---|---|---|
| 1 | (0,800] | 200*像素比 | (0,550 | 1.png |
| 1 | (800,无穷大] | 600*像素比 | (550,850] | 2.png |
| 2 | (0,800] | 200*像素比 | (0,550] | 1.png |
| 2 | (800,无穷大] | 600*像素比 | (1050,无穷大] | 3.png |
| 3 | (0,800] | 200*像素比 | (550,850] | 2.png |
| 3 | (800,无穷大] | 600*像素比 | (1050,无穷大] | 3.png |
| 视窗宽度区间 | 响应式图片的实际取值 |
|---|---|
| (0,550/像素比] | 1.png |
| (550/像素比,850/像素比] | 2.png |
| (850/像素比,1050] | 3.png |
| (1050,正无穷大] | 4.png |

无效或错误表述
以下都是无效或者错误表述:
错误一:没写默认的src,兼容性。
<img srcset="1.png 550w, 2.png 800w"/>错误二:srcSet有个单独的无条件图片地址,会引起混乱。
<img srcset="1.png 550w, 2.png" src="0.png"/>错误三:srcSet中使用px表述,无效。
<img srcset="1.png 550px, 2.png 800px" src="0.png"/>错误四:srcSet使用像素比表述时,定义了有个单独无效的表述,虽然不会导致混乱。
<img srcset="1.png 1x, 2.png" src="0.png"/>错误五:sizes表述中错误使用了w单位,导致sizes属性整体失效。
<img srcset="1.png 500px, 2.png 800px" sizes="600w" src="0.png"/>错误六:使用像素比描述的时候,试图使用sizes去改写条件,无效。
<img srcset="1.png 1x, 2.png 2x, 3.png 3x" sizes="(max-width:800px) 2x,1x" src="0.png"/>js检测手段
监测是否支持srcset:
'srcset' in HTMLImageElement.prototype
'sizes' in HTMLImageElement.prototype 检测视窗宽度:
document.body.clientWidth检测设备密度比:
window.devicePixelRatio检测当前图片的src:
document.getElementsByTagName("img")[0].currentSrc
相关链接
响应式图片的处理方案,已经有以下几种:
- https://newsn.net/say/js-retina.html
- https://newsn.net/say/html-img-srcset.html
- https://newsn.net/say/html-picture.html
看了很多文章,直到看到下面这篇文章,才明白更换浏览器的重要性。(不过,我这边的观点和这个链接文章也不一致)
结束语
苏南大叔经过不断的方案选择和对比,觉得picture+source的方案,更加好一些。毕竟谷歌浏览器里面也能顺利识别和流畅切换。更多html标签的解读,请参考苏南大叔的文章: