使用 srcset 属性可以解决一切响应式图片问题,但这里分为两种情况,确定和不确定宽度的照片。相比之下,不确定宽度的照片更复杂些。

确定宽度的照片

此处指的确定宽度照片,是指样式属性宽度设置为一个确定了多少 px 的照片。 随着越来越多的高设备像素比(指 device pixel ratio,下同)显示器出现,网站需要更高像素的照片来适配这些显示器。 比如有一张照片显示宽度为 200px,它在@1x(即设备像素比为 1 的显示器,下同) 的显示器上,是占了 200 个物理像素(即实际所占的像素,下同);它在 @2x 的显示器上,实际上是占了 400 个物理像素;在 @3x 的显示器上,实际上是占了 600 个物理像素;同样的,在 @4x 的显示器上就是占了 800 个物理像素。 如果这个照片只提供 200 像素的版本,那么在 @2x~@4x 的显示器上看起来就很模糊。如果只提供 800 像素的版本,那么在 @1x~@3x 的设备上会加载不必要的内容,也就意味着更长的不必要时间(尤其是在手机上)。 此时,就需要使用响应式图片这个方法,最简单高效的方式是使用 srcset 来解决。比如现在提供了 4 个宽度分别为 200、400、600、800 像素的照片,通过 srcset 属性,能够实现在 @1x~@4x 设备像素比的显示器上分别显示这 4 张照片,这样在每个显示器上都能显示最适合它的照片。这四张图片的文件名分别为 200px.png, 400px.png, 600px.png, 800px.png。 总结一下,就是确定宽度的照片所占物理的像素只与设备像素比有关,所以只需要 srcset 属性的 x 标识符。

<img src="200px.png" srcset="400px.png 2x, 600px.png 3x, 800px.png 4x">

这样加上 srcset 属性,浏览器就会根据自己的设备像素比来加载不同的照片,不支持 srcset 属性的浏览器就默认加载 200px.png 这张照片。这个方法是向下兼容的。

不确定宽度的照片

此处指的不确定宽度的照片,是指样式属性宽度设置为一个确定了多少百分比的照片。 如果是不确定宽度的照片的话(或者是手机上不确定图片宽度,电脑上确定宽度等),也可以通过 srcset 解决。由于像素是不能确定的,所以很难达到照片本身和照片所占物理像素是一样的,因为用户的窗口大小是不确定的。 如果只准备 5 张照片,宽度分别是 400~3200 像素,文件名是400px.png, 800px.png, 1600px, 2400px, 3200px。通常,如果没有对应图像所占的物理像素的照片的话,则会加载比所占的物理像素稍大一些的图片。例如照片占屏幕的 500 个物理像素,那么就应该选择加载 800px.png。这样加载稍大的照片仍然可以达到最佳的显示效果,同时最大限度的节省网络资源。 总结一下,就是不确定宽度的照片所占物理的像素与设备像素比和所占宽度有关,然而使用了 srcset 属性的 w 标识符后,你只需要指定宽度,浏览器会自动根据设备像素比来选择最优图片。 如果这个图片占整个窗口的 100% 大小(即 100vw 宽,下同),那么只需要这样设置 srcset 就能达到效果。 抛开设备像素比的概念吧,这个全新的 srcset 属性的 w 标识符能够让浏览器自己适应所要加载的照片。

<img src="800px.png" srcset="400px.png 400w, 800px.png 800w, 1600px.png 1600w, 2400px.png 2400w, 3200px.png 3200w" alt="" />

不过此时还有一个小问题,浏览器比较笨,它会认为图片宽度始终是整个窗口的 100% 宽,如果图片不是占整个窗口的 100%,两边留有边框怎么办呢?此时就需要使用 size 属性。 sizes 属性里制定图片的宽度,不能使用百分比单位,但可以使用 vw、px 等单位(100vw 就是整个窗口的宽度),也可以使用 calc 运算,同时支持媒体查询。要保证使用 sizes 里计算出来的宽度始终是图片所占屏幕宽度,剩下的事情都只需要浏览器完成了。 以下几种典型案例示范:

两边边框为百分比

如果图片两侧边框(指图片边缘到窗口边缘长度)始终是占屏幕的一定的百分比时,那么图片总会有一个相对于整个窗口的百分比。比如图片两侧边框始终是整个窗口的 10%,那么 size 属性就可以如下设置:

sizes="80vw"

如果图片已经在一个宽度是整个窗口的 70% 的元素下,而且元素内图片两侧还留有 10% 的边框,那么 size 属性就可以如下设置:

sizes="50vw"

两边边框为确定的像素

比如图片两侧边框始终是 10px,那么 size 属性就可以如下设置:

sizes="calc( 100vw - 10px )"

如果图片已经在一个宽度是整个窗口的 70% 的元素下,而且元素内图片两侧边框始终是 10p,那么 size 属性就可以如下设置:

sizes="calc( 70vw - 10px )"

图片有最大尺寸

比如图片两侧边框始终是 10px,但是图片最大只能是 1000px,那么 size 属性就可以如下设置:

sizes="(min-width: 1020px) 1000px, calc( 100vw - 10px )"

其中 (min-width: 1020px) 1000px 代表当屏幕像素宽度大于 1020px 时,图片宽度为 1000px。

实际效果

此处以我的网站为例,我的网站图片边框适中是 1.875rem,这个图片本身就是在一个元素下,这个元素默认宽度是整个窗口的 100%,但在屏幕宽度大于 40.063rem 的情况下,这个元素占整个窗口的 50%,并且这个元素有最大尺寸 500px,现在把 size 属性和用 w 标识符的 srcset 属性放在一起,就完美解决了。

<img src="800px.png" sizes="(min-width: 1000px) calc( 500px - 1.875rem ), (min-width: 40.063rem) calc( 50vw - 1.875rem ), calc( 100vw - 1.875rem )" srcset="400px.png 400w, 800px.png 800w, 1600px.png 1600w, 2400px.png 2400w, 3200px.png 3200w" alt="" />

然后你就可以在浏览器中测试了,达到了适配所有屏幕尺寸,所有设备像素比的效果。

关于兼容性

通常情况下,srcset 属性的 x 标识符支持的比 w 标识符更好,因为 w 标识符是一个新的属性。不过好消息是 Chrome 38+、Firefox 38+、Safari 9+、Opera 30+、Android 5.0+、iOS 9+(没错,IE/Edge、Windows Phone 截止到写文章之日都不支持) 都纷纷支持了 w 标识符,所以我认为现在很适合换用 srcset 属性的 w 标识符来做响应式图片了。而且这个方法是完美向下兼容的,即使不支持也没关系。 你也可以在 CanIUse.com 上查询关于 secset 属性的完整的兼容性列表