三羊

三羊的小站

响应式图片加载

July 24, 2017/「 resposivemobile / Edit on Github ✏️

在做响应式图片加载需要使用到的两个关键属性就是srcsetsizes ,这两个属性是 H5 中新加的,是给 img 标签提供多个不同尺寸的图片,在不同设备,不同分辨率的情况下加载不同的图片。

根据屏幕分辨率选择

当只需要根据屏幕分辨率去选择不同的图片的时候,有 2 种情况。

第 1 种,语法形式为:

srcset="[url] [dpr]x,[url] [dpr]x,....etc"

一般屏幕的分辨率会到 3 倍,例如 iphone6plus,其 dpr=3,这种是直接给出每种图片的分辨率规格,例子如下:

<img
  src="./mm-width-128px.jpg"
  alt="示例图片"
  srcset="./mm-width-256px.jpg 2x, ./mm-width-512px.jpg 3x"
/>

上面的示例表示,在 dpr=1 的屏幕下加载 128px 这张图片;在 1<dpr<=2 时,加载 256px 这张图片;当 dpr>2 时就加载 512px 这张图片了。 注意,srcset 里的x一定要是小写,大写的话,chrome 会无法识别。 查看 demo

第 2 种,语法形式为:

srcset="[url] [width]w,[url] [width]w,...etc"

注意,这里的单位为w,与第一种不同。这里的w只是我们告诉浏览器这个图片的实际宽度,我们也可以给定不同的值,它仅用于我们给浏览器提示这个图片的实际大小,然后浏览器根据当前设备去选择最佳的图片。如果,我们给的是一个错误的值,或对浏览器撒谎,浏览器当然会选择错误的图片。浏览器最后也会根据我们给的这个值去转化为第 1 种形式中的等价的值,即像素密度。这种形式,浏览器会默认根据 100vw 的宽度去计算,也就是设备的 viewport 宽度,也就是说浏览器会默认图片会铺满整个设备的宽度去显示,例如,

<img
  src="./mm-width-128px.jpg"
  alt="示例图片"
  srcset="
    ./mm-width-128px.jpg 320w,
    ./mm-width-256px.jpg 640w,
    ./mm-width-512px.jpg 750w
  "
/>

上面的示例,浏览器先会自动计算出每种图片在当前设备下显示的像素密度,比如当前用户在设备宽度为 320px 的设备下查看,则计算结果如下:

320 / 320  = 1
640 / 320  = 2
750 / 320  = 2.34375

如果当前设备的 dpr=1,所以显示 128px 这张图了;如果当前设备的 dpr=2,这会选择 256px 这张图片了,如果当前设备的 dpr=2.5,则会显示 516px 这张图了,实际上与第 1 种形式中判断的方式一样。 注意,xw不能混合使用。

根据媒体查询来选择

媒体查询来选择就会用到 sizes 来写媒体查询语句了,sizes 的语法格式为

sizes="[media query] [length],[media query] [length],...etc"

这里的 length 可以是remempxvw,甚至是 css3 中的calc,表示我们想让这个图片在浏览器中显示的宽度,注意实际的图片显示宽度并不会受这个值得影响,只会根据图片自身的实际大小或 css 规则去显示。这个 length 值,只是我们提供给浏览器的一个计算依据值,浏览器根据这个值,再结合 srcset 提供的图片规格,去选择在当前设备下最合适的图片。在使用这种方式时一定要与 srcset 配合使用,才能生效。

<img
  src="./mm-width-128px.jpg"
  alt="示例图片"
  srcset="
    ./mm-width-128px.jpg 320w,
    ./mm-width-256px.jpg 640w,
    ./mm-width-512px.jpg 750w
  "
  sizes="(width:640px) 640px,(width:750px) 750px,320px"
/>

上面的例子表示,首先根据当前设备的 viewport 去匹配 sizes 的媒体查询,找到第一个匹配的值,然后应用它后面的 width 作为图片会显示宽度的去计算出x单位等价的值,即像素密度,计算规则跟上面的一样,例如,当前设备的 viewport 是 640px,则匹配到 width=640px,计算结果是:

320 / 640  =  0.5
640 / 640  =  1
750 / 640  =  1.171875

然后根据当前设备的 dpr 去选择最接近的图片,如果 dpr=1,则选择 256px 这张图片,如果 dpr>1,则选择 512px 这张图片。上面的 sizes 查询选择 640px,750px 作为断点,也就是 viewport=640px 时匹配 width=640px,viewport=750px 时匹配 width=750px,否则就是 width=320px。实际,更为通用的端点是一个范围查询匹配,而不是一个固定的值,例如下面的例子:

<img
  src="./mm-width-128px.jpg"
  alt="示例图片"
  srcset="
    ./mm-width-128px.jpg 320w,
    ./mm-width-256px.jpg 640w,
    ./mm-width-512px.jpg 750w
  "
  sizes="(max-width:750px) 100vw,60vw"
/>

这个 sizes 的意思是:如果当前设备的 viewport<=750px,则 width=100vw(表示图片铺满这个设备宽度显示,1vw=1%*viewport),否则 width=60vw。比如当前设备的 viewport=780px,则计算结果是:

320 / (0.6 * 780)  =  0.6837606837606838
640 / (0.6 * 780)  =  1.3675213675213675
750 / (0.6 * 780)  =  1.6025641025641026

类似的,再根据当前设备的 dpr 去选择图片了,如果 dpr=1,则选择 256px 这张图片了,如果 dpr>1,则选择最接 近的 512px 这张了。

那如果当前设备 viewport=400px,则匹配的 width=100vw,计算结果是:

320 / (1.0 * 400)  =  0.8
640 / (1.0 * 400)  =  1.6
750 / (1.0 * 400)  =  1.875

再根据设备的 dpr 去选择浏览器认为最佳的那张图片了。 这种 sizes 方式有一点要注意,浏览器只会应用一地个匹配的 sizes,也就是说如果同时能匹配多个媒体查询的条件,但只会应用第一个匹配声明 的 width。

实际上,上面的思想就是,我们给定一组规格不同的图片(这个就是 srcset 设置的),以及表示我们想要它们在浏览器中显示的宽度(这个就是 sizes 设置的,如果没有设置 sizes,则默认为 100vw),浏览器根据我们提供的这些信息去选择最佳的图片。我们一直强调的是浏览器去选择,所以最后用哪张图,还是得浏览器说了算,这样的目的是,为将来浏览器能更加智能的选择,比如,浏览器可能根据当前的设备网络环境去选择最优的,可能 3G 环境只选择普清的图片,而在 wifi 环境总是选择高清的图片,或者当前用户设置的方式去选择最优的,可能用户自己设置只加载普清的图片,等等。如果,要浏览器完全去根据我们设置的信息去选择,那么使用picture标签,picture标签会完全根据我们提供的 srcset 和 sizes 去选择了,例如:

<picture>
  <source media="(min-width:30em)" srcset="cake-table.jpg" />
  <source media="(min-width:60em)" srcset="cake-shop.jpg" />
  <img src="scones.jpg" alt="One way or another,you will get cake." />
</picture>

这个例子表示,如果当前设备屏幕大于等于 30em,则显示 cake-table 这张图片,如果大于等于 60em,则选择 cake-shop 这张图片,否则就显示 scones.jpg 这张图片,这里的img标签一定是要的,对于不支持picture设备,会忽略picture,直接显示img指定的图片。

由于,srcset 和 sizes 是 h5 中新加的,并不是所有的浏览器都支持,所以,在使用的时候,我们一直给img标签加了 src 属性,因为对于不支持 srcset 和 sizes 的设备,至少会显示 src 指定的图片。看看浏览器对它的支持情况吧

浏览器对srcset支持情况

参考链接

若有收获,小额鼓励