Tencent前端笔试题竟然考了3道关于srcset + sizes + w标识符实现响应式图片的不定项选择题,当时就蒙了,完全没接触过这种方法来实现响应式图片呐。在此之前的记忆和实践中,一般都是用media query有时还会配合JavaScript判断来实现按屏幕宽度加载相应图片,但是遇到高分屏的情况就比较鸡肋,记得工作室网站当时就直接放弃了兼容高分屏。

考完以后,开始疯狂查找图片响应加载的方法,发现srcset + sizes + w标识符实现响应式图片的方法完美向后兼容,可以给网页渐进增强,这种技术在国内也算成熟,文档也算多。另外,在查资料过程中发现,其实media query也可以实现高分屏的倍屏判断。

get到新技能,开心,于是,作此学习笔记。

尽管Web设计的初衷是让所有人都能通过任何有效的途径来访问,但是设备的碎片化迫使业界潮流转向了响应式设计。其中,尤以响应式图片最为鸡肋。由于位图是固定分辨率的,而野生的图片容器img和src没有适配不同设备屏幕的能力。所以页面仔们面临这样一个选择:让页面在有些情况下变得模糊,还是在绝大多数情况下变慢。大多数人倾向于选择后者,给所有设备发送能适配更大、更高分辨率的图片。浪费流量和牺牲下载速度呀。

所以,响应式图片的实现已迫不及待。连Win10 Edge浏览器都已开始支持Srcset属性,为全面支持响应式图片设计迈出的第一步。这个说明,Srcset很快就会风靡前端圈,时代在进步。

现在流行的响应式图片技术主要分为三种:CSS、JavaScript和服务端。其中,JavaScript实现的响应式图片技术(比较流行的有HiSRC.js和Picturefill.js)在用户禁用JS时就不起作用了;而服务端进行相应的图片适配处理确实是个好方法,但作为优雅的页面仔,能在前端完成的就不要去坑人家后端的童鞋嘛。目前业界流行的响应式图片,依旧是CSS技术。

方案1:width:100%

最简单的CSS解决图片自适应方案,莫过于在CSS中设置图片的最大宽度为100%,如下:

img {
    max-width: 100%;
}

设置图片的最大宽度为100%,图片就会根据父级元素的大小来自动调节自身的大小适应设备的宽度。如果需要支持IE6~8这类浏览器,还需要加一句width:100%,因为IE6~8不支持max-width:100%。

当然,这种方案是不完美的,当希望在高分辨率屏幕上也能正常的显示高质量的图片的时候,原本只是150 150的图片,现在你得准备300 300的图片,这样才能在高分辨率屏幕上完美显示。那就意味着我们的图片是非常大的,如果让所有的设备都用这样一种高质量图片,得牺牲不少流量和加载速度。无疑,好方法应该在后头。

方案2:media query

CSS3中新出现的媒体查询,只需要准备不同分辨率和设备屏幕宽度下的图片,便可实现图片的响应式加载。事实上这也是业界主流的做法。

media query支持的属性如下表所示:

属性 Min/Max 描述
color 整数 yes 每种色彩的字节数
color-index 整数 yes 色彩表中的色彩数
device-aspect-ratio 整数/整数 yes 宽高比例
device-height length yes 设备屏幕的输出高度
device-width length yes 设备屏幕的输出宽度
grid 整数 no 是否是基于格栅的设备
height length yes 渲染界面的高度
monochrome 整数 yes 单色帧缓冲器中每像素字节
resolution 分辨率(“dpi/dpcm”) yes 分辨率
scan Progressive interlaced no tv媒体类型的扫描方式
width length yes 渲染界面的宽度
orientation Portrait/landscape no 横屏或竖屏

其中,min-和max-都可以配合上表中的属性使用,与max-width和minwidth等类似。

为了识别高清屏幕,我们可以用下面这样的语法来表示:

@media
only screen and (-webkit-min-device-pixel-ratio: 2),
only screen and (min–moz-device-pixel-ratio: 2),
only screen and (-moz-min-device-pixel-ratio: 2),
only screen and (-o-min-device-pixel-ratio: 2/1),
only screen and (min-device-pixel-ratio: 2),
only screen and (min-resolution: 192dpi),
only screen and (min-resolution: 2dppx) {
/ styles for Retina-type displays /
}

media query的详细教程可以参考:media type与media query

CSS4的background-image新规范草案中提到image-set,目前支持的浏览器有safari6+、chrome21+和Opera15+,IE6~11不支持。image-set为Web前端人员提供了一种解决高分辨率图像的显示。如:

.img {
  background-image: url(image.png);
  background: image-set(url(image-1x.png) 1x,url(image-2x.png) 2x);
}

这种方法最大的不足就是响应式图片需要是背景图片,而不能是响应式加载<img>图片。

方案3:srcset + sizes + w标识符

前面做了这么多铺垫终于来到本文的重点了。

media query解决了背景图片的响应式问题,但是 HTML中的 img 元素怎么办呢?2011年11月 @brucel 提出了HTML5 的一个解决草案:

<picture alt="">
    <source src=hires.png media="min-width:800px">
    <source src=midres.png media="min-width:480px">
    <source src=lores.png>
    <!-- 不支持的浏览器降级处理 -->
    <img src=midres.png alt="">
</picture>

<picture>里面的资源是具有fallback特性的,即第一个图片资源无法加载的时候可以跳过加载后面的备用资源。其中,media属性中的属性值可以是任意的media query。这个方法不错,最大的缺陷是引入了冗余的DOM元素。

于此同时,其他的一些想法如雨后春笋般涌现出来,于是 W3C的Responsive Images Community Group 也制定了相应的规范。官方规范:https://html.spec.whatwg.org/multipage/embedded-content.html#embedded-content

但是 Apple 的 eoconnor 提出的方案是这样的:

<img src="foo-lores.jpg" 
    srcset="foo-hires.jpg 2x,  foo-superduperhires.jpg 6.5x"           alt="">

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

确定宽度的图片

此处指的确定宽度图片,是指样式属性宽度设置为一个确定px的图片。

确定宽度的照片所占物理的像素只与设备像素比有关,所以只需要 srcset 属性的 x 标识符。

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

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

不确定宽度的图片

此处指的不确定宽度的图片,是指样式属性宽度设置为确定百分比的图片。

如果是不确定宽度的照片的话(或者是手机上不确定图片宽度,电脑上确定宽度等),也可以通过 srcset 解决。由于像素是不能确定的,所以很难达到照片本身和照片所占物理像素是一样的,因为用户的窗口大小是不确定的。

不确定宽度的照片所占物理的像素与设备像素比和所占宽度有关,使用了 srcset 属性的 w 标识符后,只需要指定宽度,浏览器会自动根据设备像素比来选择最优图片。注意,每张图片的宽度都要用w描述符来声明,w的值是图片以px为单位的真实宽度。但是w标识符只是一个描述符,不是单位。具体解释可以参考:响应式图片srcset全新释义sizes属性w描述符

如果这个图片占整个窗口的 100% 大小(即 100vw 宽),那么只需要这样设置 srcset 就能达到效果。

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

不过此时还有一个小问题,浏览器比较笨,它会认为图片宽度始终是父容器 100% 宽,如果图片不是占父容器的 100%,两边留有边框怎么办呢?此时就需要使用 size 属性。

sizes 属性里制定图片的宽度,不能使用百分比单位,但可以使用 vw、px 等单位(100vw 就是整个父容器的宽度),也可以使用 calc 运算,同时支持媒体查询。要保证使用 sizes 里计算出来的宽度始终是图片所占父容器的宽度。

以下几种典型案例示范:

1. 两边边框为百分比

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

sizes="80vw"

2. 两边边框为确定的像素

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

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

4. 图片有最大尺寸

sizes还可以做一些媒体查询。比如图片两侧边框始终是 10px,但是图片最大只能是 1000px,那么 size 属性就可以如下设置:

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

5. 综合实例如:

<img src="800px.png" srcset="400px.png 400w, 800px.png 800w, 1600px.png 1600w, 2400px.png 2400w, 3200px.png 3200w" sizes="(min-width: 1020px) 1000px, calc( 100vw - 10px )" alt="hello world!">

srcset兼容性

Chrome 38+、Firefox 38+、Safari 9+、Opera 30+、Android 5.0+、iOS 9+都纷纷支持了srcset 属性,而且Win10 Edge浏览器也开始支持srcset属性。各大浏览器开始向全面支持响应式图片设计,所以我们应该有信心使用srcset属性。而且这个方案是完美向下兼容的,即使浏览器不支持srcset属性也是可以加载src属性定义的图片。

srcset属性的兼容性见caniuse的统计:http://caniuse.com/#feat=srcset

一起拥抱srcset吧!

在Chrome浏览器中模拟高分屏和不同尺寸设备

Chrome浏览器下按F12或者右键鼠标选择“审查元素”,打开开发者工具,切换至设备模式:
chrome-device-selector-guide

然后F5刷新浏览器,即可。

参考文章

  1. 使用 srcset + sizes 属性与 w 标识符解决一切响应式图片问题
  2. 响应式图片srcset全新释义sizes属性w描述符
本文作者:子匠_Zijor,转载请注明出处:http://www.dengzhr.com/others/mobile/respond/352