pekonchan/Blog

响应式图片之srcset & sizes详讲

pekonchan opened this issue · 0 comments

前言

关于srcset & sizes的资料其实不少,但也不多,其中详细讲解其选择图片的机制的文章,更是少数了,连MDN都是粗粗一笔带过,到底哪里文章说的是正确的,哪里有误导人的,其实很难找到一个参考。

于是乎自己亲自去动手研究一下了,不能光看资料,可信度还是值得考虑。

当然出于本人能力限制,也许研究不够透彻导致带来的错误之处,请多多指出,本人虚心请教。

简介

H5中img有两个新属性,srcsetsizes,主要作用:

  • 控制响应式图片,根据屏幕大小来选择显示不同图片
  • 根据屏幕的不同(视网膜屏幕还是普通屏),展示不同质量的图片,合理控制下载资源,以及带给用户高质量享受。

文本有一些名词概念,可能需要事先有了解,方能更好的理解。可先大致浏览此 前端认知:PPI、DPI、设备像素等概念

srcset

(1)格式一:图片源地址 空格 图片像素宽度[, 图片源地址 空格 图片像素宽度, ...],如

srcset="1.jpg 580w, 2.png 618w"

表示图片1.jpg的像素宽度为580px,图片2.png的像素宽度为618px,两个图片源之间用逗号隔开。

千万注意:描述图片的像素宽度是用'w'单位,且一定要是图片的真实像素宽度,如果私自改动,那么会影响浏览器对图片的选择!

其实上述例子类似给了个默认的sizes属性(具体下面再讲)

srcset="1.jpg 580w, 2.png 618w" sizes="100vw"

(2)格式二:图片源地址 空格 屏幕像素密度[, 图片源地址 空格 屏幕像素密度, ...],如

srcset="1.jpg 1x, 2.png 2x"

表示图片1.jpg为DPR为1时下显示的图片;2.png为DPR为2时显示的图片,如果没有更大的DPR设置的图片源,那么当大于当前设置的DPR最高值时,会采用当前设置的最大DPR的图片源。如这里如果屏幕DPR为3,那么还是会用2.png

小结

在浏览器支持srcset的情况下,src值就成为了一个1x情况下的候选图片,在没有符合条件的情况下,会采用该值。

关于'x'的图片选择,很简单,按照上述规则就好了。但是关于'w'的图片选择,就没有那么单纯了,需要结合下述的sizes属性一起来分析。

sizes

只有当设置了srcset,且单位为w时,sizes的设置才会起效。浏览器到底是怎么选择图片的呢,是先根据sizes设定的条件下,找出此刻图片显示的宽度,然后根据这个宽度去srcset里找符合条件的图片。

什么样的才是叫符合条件呢?

srcset中设置的图片像素宽度('w'的值),组成了相应的半开半闭区间(a, b]。 图片的显示宽度(sizes规定的值)看落在哪个区间内,取区间中最大值对应的图片。若没有最大值(如∞),则取上个区间最大值。

<img src="4.jpg" srcset="3.jpg 229w,2.png 618w,1.jpg 1000w", sizes="300px">

上面设置图片要显示成300pxsizes里的值),在srcset里的几个临界值中形成了(0, 229px](229px, 618px](618px, 1000px](1000px, ∞]300px落在了(229px, 618px]中,取最大值618,因此最终选择的图片就是2.png

如果sizes改成1200px,按照上述规则,最终会选取1000w1.jpg

注意:srcset里的顺序不重要,不会受到影响。

好了,我们知道浏览器的选取规则。还是说一下sizes语法结构

[媒体查询 空格 ]图片显示宽度[, [媒体查询 空格 ]图片显示宽], ..., 其余条件宽度值]

图片的宽度值单位不能为%,其余正常单位可以使用

举例:

sizes="(max-width: 500px) 400px, (max-width: 900px) 700px, 1200px"

上面的意思就是,在屏幕小于等于500px情况下,图片显示成400px宽;在屏幕小于等于900px情况下,图片显示成700px宽;其余情况显示成1200px宽。

因此,可根据这种sizes值,判断在哪个屏幕大小下显示什么宽度的图片,然后根据这个宽度值去srcset里找满足条件的图片源。

这个例子

<img src="4.jpg" srcset="3.jpg 229w,2.png 618w,1.jpg 1000w", sizes="(max-width: 500px) 400px, (max-width: 900px) 700px, 1200px">

在屏幕小于等于500px情况下,图片显示成400px宽,选取2.png;在屏幕小于等于900px情况下,图片显示成700px宽,选取1.jpg;其余情况显示成1200px宽,还是选取1.jpg

注意:sizes里的媒体查询条件顺序是很重要的,只要满足了越靠前的某个条件,那么后面的条件会被忽略的

还有,如果sizes属性没有值,或者在有媒体条件下,其余条件宽度没有设值,那么默认是100vw

<img src="4.jpg" srcset="3.jpg 229w,2.png 618w">

<!--等同于-->

<img src="4.jpg" srcset="3.jpg 229w,2.png 618w" sizes="100vw">
<img src="4.jpg" srcset="3.jpg 229w,2.png 618w", sizes="(max-width: 500px) 400px">

<!--等同于-->

<img src="4.jpg" srcset="3.jpg 229w,2.png 618w", sizes="(max-width: 500px) 400px, 100vw">

这种时候,就是直接根据屏幕的宽度来决定选取哪个图片源了。

DPR的影响

上述内容,都是在DPR为1的情况下说明的,主要针对PC端吧。那如果是手机端,DPR不单单是1,还有2,3的情况。这时候其实上述内容的规则还是不会变的,但是我们需要做一些值的转化才能套用上述规则。这里主要是说'w'的情况下浏览器如何去选择

以该例子说明

<img src="4.jpg" srcset="3.jpg 229w,2.png 618w" sizes="(max-width: 600px) 114px">

首先我们要知道,srcset里的'w'符号,是代表的是图像的宽度像素,是个物理像素;sizes里的114px是表示逻辑像素!

而浏览器对图片的选择,是要转化为同一概念上的像素才能进行对比的,总不能拿着逻辑像素值去物理像素区间里做比较吧?

因此,在DPR不为1时,要转化一下像素值才能去区间里比较,套用上述规则。

如在DPR=2时,把114px转化为物理像素,即114px * 2 = 228px,用228px去(0, 229px]、(229px, 618px]、(618px, ∞]里做比较,落在了(0, 229px]中,取229px对应的3.jpg

规范

如果你想用这两个属性好好控制图片,那你得遵循好以下规范,不然很多效果是你难以预测得到的,毕竟那么多浏览器,兼容性也各自不同。

都是关于srcset的值的规范:

  • 不同图片源,要都是同一个类型的,如都是'x'的描述,或都是'w'的描述。
  • 不同图片源,不能是同一个值,如同'1x' 或 同 '900w'
  • 同个图片源,不能既有'w'又有'x'
  • 用'w'描述时,一定一定要是图片真实的宽度值,你可以右键图片查看属性,看到其宽多少像素,就是这个像素值了。如果你不填写真实的情况,那么会影响浏览器对图片的选择,以及显示在浏览器上的图片的宽度也会进行一定比例缩放,比例为真实宽度:私自改动宽。

如果你不信这个规范,毕竟很多其他文章没这么要求限制,你难免会产生怀疑,虽然打破这些规范,浏览器可能也会显示某种规律,但是,考虑到浏览器的兼容性问题,实在不建议不按照上述要求写。

【这块内容无关重要】
以下是我亲测的一些情况。感兴趣的你自己也可以试试看,当然我这里测试的浏览器有限

  1. srcset值有不符合当前屏幕的'x'时,还有'w'时,会按照'w'的条件显示图片,其余情况会显示这个最小的不符合的'x'下的图片;但360会一直用这个最小的不符合的'w'值。
  2. 有符合的'w'又有符合的'x','x'的优先级最高。无论怎样都会显示'x'的条件下的图片
  3. 同个图片源,既有'w'又有'x'是无效的
  4. 不同图片源,同一个值,如同'1x' 或 同 '900w',最前面的那个优先

兼容性

这里可以看到一个大体的情况: 兼容性

但还有一些值得一说的内容,毕竟上面的链接里是没有的

srcset值仅有'w'时

在chrome屏幕变化时(如你拖动浏览器窗口),图片能从设置的小屏到大屏进行图像替换,但大屏往小屏变化,不会进行图片替换。

如下述例子,从低于500px宽度的屏幕上是1.jgp,屏幕变化到500px-900px时,图片会替换为2.png,到900px以上的屏幕时,替换为3.jpg;但从900px上 -> 500px-900px -> 500px下,图片却不会进行替换。

<img src="4.jpg" srcset="1.jpg 400w,2.png 600w,3.jpg 900w">

但Firefox不存在chrome的从大屏到小屏图片不替换的问题。

360浏览器支持不好,只会显示srcset中的最大屏的图片,如上述例子中的3.jpg

其他情况

  1. srcset值都是'x'时,且是不符合当前屏幕的条件,会采用src的值,但360会还是用这个不符合的srcset值。
<img src="4.jpg" srcset="3.jpg 3x,1.jpg 2x">
  1. srcset值都是'x'时,有一个符合的,就采用符合的。但是360的表现很奇怪,找不出规律。