响应式布局提高篇 – 图片正确的打开方式

作者 | Brilliant Open Web团队

本文承接上一章的内容,接着介绍响应式布局设计,主要讲如何实现响应式图片。通过对图片适配问题的说明,加深对响应式图片的理解,并分析对比了响应式的使用场景以及实现方法,以选择合适的方案让图片更好地适配不同的设备。

图片在 Web 中占有非常重要的地位,一方面,一图胜千言,图片作为内容的载体,能够有效传递出信息;另一方面,图片占网页流量消耗的 60%,对网页的大小和加载速度有很大的影响。

随着终端设备日益丰富,人们更多地通过各种尺寸的小屏浏览网页,经常发生图片过大导致网页布局混乱的情况,非常影响用户体验。因此图片需要根据设备类型和尺寸进行自适应调整,才能保证网站在各个终端获得相似的使用体验。

要做到图片自适应宽度,一个简单的方法是,给图片设置 max-width: 100%,这确实非常有作用,图片不会溢出或者页面出现横向滚动条,那还有没有其他需要我们注意的呢?

(图片来源:http://www.108themes.com/windows-10-themes/italy/

 

图片的质量

在使用高清屏幕设备(比如 iPhone X)浏览网站时我们会发现,一些图片看起来比较模糊,而在普通屏幕设备中却没有那么模糊,这是为什么呢?

这里需要用到在上一章中提到的几个概念,为方便起见,我们一起来回顾一下:

物理像素(设备像素)

一个物理像素代表的就是最小的物理显示单元,在同一个设备中,物理像素点的大小是固定的,数量也是固定的。比如 iPhone X 的物理像素是 1125×2436。

逻辑像素(CSS 像素、设备独立像素)

逻辑像素是一个抽象单位,与设备无关,在不同的设备中呈现的大小是一致的,比如逻辑像素为 10×10 的方块,在手机和显示器看上去大小是一样的,这样可以保持阅读体验的一致,同时也方便开发。iPhone X 的逻辑像素是 375×812。

设备像素比(Device Pixel Ratio,DPR)

根据以上两个概念,可以自然地想到,在一个设备中逻辑像素与物理像素的比值是确定的,一个逻辑像素覆盖了多个物理像素,这可以用设备像素比来表示。比如 iPhone X,375×812 的逻辑像素包含了 1125×2436 的物理像素,设备像素比的值就是 3。可以通过下图进一步理解上面三个概念之间的关系。

有了上面的概念,就能简单地解释图片为什么会变模糊。举个例子,分别在普通屏(DRP = 1)和 Retina 屏(DPR = 2)用 CSS 像素为 300×400 的 <img> 显示分辨率是 300×400 的图片。

对于普通屏,图像像素和物理像素 1:1,图像不失真。而对于 Retina 屏,图像像素和物理像素 1:4,由于每个图像像素不可分割,物理像素就近取色,从而让图片变得模糊。这种情况需要使用分辨率更高的图片,比如分辨率为 600×800,图像像素和物理像素 1:1,这样就能充分利用 Retina 屏幕的物理像素点,显示出更清晰的图片。

如果在普通屏下也使用分辨率为 600×800 的图片,图像像素和物理像素 4:1,物理像素点只能通过采样,用不足的数量来显示图片。这不仅没有发挥出高清图片的优势,还由于图片的增大而造成了带宽的浪费。

由此可以发现,为了更好地在各种终端设备显示图片,需要考虑设备像素比、图像分辨率、渲染尺寸等因素,如果只是简单地加载图片然后修改样式适应宽度,并不能保证显示效果和加载速度,甚至会大大降低用户的使用体验。因此,需要采用合适的方法对图片进行响应式设计。

 

媒体查询(media query)

根据前面的分析,图片的响应式设计与渲染尺寸(图片显示大小,<img> 的 CSS 像素)有关,而渲染尺寸通常由设备的视口大小决定,因此就需要一种方法对设备的视口大小进行判断。

CSS 中的媒体查询提供了相关的判断方法,可以根据不同的设备特征应用不同样式。媒体查询支持很多设备特征,由于本章主要介绍响应式图片,实际主要用到其中的视口的宽度和设备方向,如下表所示。

更多关于媒体查询的介绍可以参考 MDN 上的文档《CSS 媒体查询》(https://developer.mozilla.org/zh-CN/docs/Web/Guide/CSS/Media_queries)

设备特征 取值 说明
min-width 数值,如 600px 视口宽度大于 min-width 时判断为真
max-width 数值,如 800px 视口宽度小于 max-width 时判断为真
orientation portrait | landscape 当前设备方向,portrait 垂直,landscape 水平

对于 min-width 和 max-width 的取值,我们称为断点(Breakpoints)。如何选择断点,主要取决于产品设计本身,没有万能媒体查询的代码。但经过实践,我们也总结了一套比较具有代表性的设备断点。

如果要对细分屏幕大小进行适配,可以查看这篇文章,里面列出了详细的常见设备的媒体查询条件,《media queries for common device breakpoints》(https://responsivedesign.is/develop/browser-feature-support/media-queries-for-common-device-breakpoints/)

 

实现响应式图片

前面说了这么多,终于到大家最为关心的部分了。不过先别急,在动手之前,还有一个问题是需要我们去思考的,那就是在根据设备特征选用不同的图片时,是否需要更改比例、裁剪甚至替换整个图片,或者只是仅仅更改图片的分辨率。前者被称为艺术方向(art direction),后者被称为分辨率切换(resolution switching),下面先介绍分辨率切换。

分辨率切换

分辨率切换主要用于优化低分辨率设备的带宽消耗,通常使用在如下场景:开发者提供图片的多个分辨率版本,大屏和高清屏用户获取高分辨率版本,小屏用户获取低分辨率版本。以下是实现分辨率切换的代码例子:

<img srcset="1200x800.jpg 1200w,             700x500.jpg 700w,             500x400.jpg 500w,             300x200.jpg 300w"     sizes="(max-width: 480px) 350px, (max-width: 800px) 650px, 1000px"     src="500x400.jpg"     alt="An example image">

这里使用到了 <img> 的 sizes 和 srcset 属性,主流浏览器都支持得很好,而在不兼容的浏览器里,会自动降级使用默认 的 src 属性,因此可以放心大胆地使用。

下面具体介绍这两个属性的用法。

srcset

一般形式:

srcset="[url] [num][descriptor], [url] [num][descriptor], …"

指定了一组可选的源,源之间用逗号分隔,每个源由两部分组成:

  1. 图片的 url
  2. 正整数+宽度描述符 w 或者 正浮点数+像素密度描述符 x,如果该项为空,则默认为 1x

浏览器会通过描述符来选择对应的源,如果使用密度描述符 x,则判断哪一项与设备像素比更接近,例如:

<img srcset="1200x800.jpg 2.5x,             800x600.jpg 2x,             600x400.jpg 1.5x"     src="500x400.jpg">

在 DPR = 2 的 iPhone 6 中会加载 800×600 的图片。

在 DPR = 3 的 iPhone X 中会加载 1200×800 的图片。

对于宽度描述符 w,由于需要与 sizes 属性一起使用,因此会在下面和 sizes 属性一起介绍。

sizes

一般形式:

sizes="[media-query] [size], [media-query] [size], …, [size]"

指定了一组可选的 size,size 之间用逗号分隔,每个 size 由两部分组成:

  1. 媒体查询,但是不能存在于最后一项,因为最后一项用作默认值
  2. 图片的 size(CSS 像素),可用 px、em、vw 等单位

当 srcset 属性使用 w 时,sizes 属性才会起作用,浏览器会根据下面的顺序加载图片。

  1. 浏览器逐项判断 sizes 属性中的媒体查询条件,当为真时取该项的 size 作为图片的大小信息,如果 <img> 的 CSS 样式没有指定大小,则会使用 size 作为 <img> 的大小
  2. 根据 size 和设备像素比,两者相乘得出图片的分辨率
  3. 从 srcset 的宽度描述符中选出不小于所得分辨率中最接近的一项,没有则选最大的一项,取该项作为图片的源

例如:

<img srcset="1200x800.jpg 1200w,             700x500.jpg 700w,             500x400.jpg 500w,             300x200.jpg 300w"     sizes="(max-width: 480px) 350px,             (max-width: 800px) 650px,             1000px"     src="500x400.jpg">

在 DPR = 1 和视口宽度 = 440px 的情况下,根据 sizes 属性获得 size 为 350px,计算 350×1 = 350,在 srcset 中不小于 350 又最接近的是 500w,所以会选择加载 500w 对应的 500×400 图片。

在 DPR = 2 和视口宽度 = 700px 的情况下,根据 sizes 属性获得 size 为 650px,计算 650×2 = 1300,在 srcset 中各项均小于 1300w ,所以会选择最大的 1200w 对应的 1200×800 图片。

以上就是 srcset 和 sizes 的用法,需要注意的是,以下情况是不正确的:

  • 在同一个 srcset 中混合使用了 x 和 w
  • 在同一个 srcset 有重复的描述符,比如有两项都是 2x

另外值得一提的是,当我们使用 srcset 属性时,实际上是向浏览器提供相关信息,让浏览器作出更好的选择,浏览器可能还会根据用户的偏好、网络条件等因素调整选择。而这个特点,就是接着要介绍的 <picture> 与 <img>+srcset 之间的重要区别之一。

如果对 srcset 和 sizes 还想了解更多,可以访问 MDN 的文档《响应式图片》(https://developer.mozilla.org/zh-CN/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images)

艺术方向

艺术方向听上去可能比较让人费解,但如果用例子来解释就会非常简单。考虑如下情况:

我们提到将图片的 max-width 设置为 100%,图片就会在手机屏幕上压缩到视口的宽度,如果这张图片实际上很大,图片中的内容就会看不清,特别是如果图片主要内容集中在中间,如人像,浏览效果会比较差。遇到这样的情况,最好的方式是在不同的屏幕尺寸下采用不同的图片,让主要内容保持在视口中间,如下图。

(图片来源:https://developers.google.com/web/fundamentals/design-and-ux/responsive/images

所以艺术方向可以理解为根据设备特征选用突出重点的不同图片,这些图片通常经过更改比例、裁剪等处理。

可以发现,与前面所说的分辨率切换不同,艺术方向对图片选择的要求更为严格。如果没有显示最合适的图片,对于分辨率切换,也只是看上去有点模糊或者消耗了更多流量,而对于艺术方向,就不能显示出主要内容,非常影响体验。

一般用 <picture> 来实现艺术方向,<picture> 的兼容性也很好,即使浏览器不兼容,也能回退到 <img>

Picture

<picture> 是一个图像和图像源的容器,包含多个 <source> 和一个 <img>,自身不会有任何的显示,只是提供了一个环境,让里面的 <img> 从中选择正确的源。

<picture>  <source media="(max-width: 500px)" srcset="cat-small.jpg">  <source media="(min-width: 1000px)" srcset="cat-big.webp" type="image/webp">  <img src="cat.jpg" alt="An cat img"></picture>

浏览器从上到下检查 <source>,根据 mediatype 和 srcset 属性进行匹配,发现符合就选择这个 <source> 的源作为 <img> 的源,如果都不符合,则不改变 <img> 的源。

<picture> 的使用主要集中在 <source> 上,<source> 具有三个属性:mediatype 和 srcset,其中 srcset 和前面提到的类似,就不再介绍了,下面说说 media 和 type 两个属性。

media

类似于媒体查询,作为浏览器的判断条件,浏览器会严格按照 media 来选择 <source>,如果 media 不符合,浏览器会跳过该项,然后继续检查下一个 <source>

比如,要求视口宽度大于 800px 的情况:media=”(min-width: 800px)”

type

指定了 <source> 图片源的 MIME type,如果浏览器不支持这个格式,则跳过该项,然后继续检查下一个 <source>

比如,指定图片是 WebP 格式:type=”image/webp”

在上面例子中,如果是支持 WebP 格式且视口宽度 = 1200 的设备,浏览器首先检查第一个 <source>,由于 1200 大于 500,所以跳过这个 <source>,接着检查下一个 。由于设备支持 WebP 格式,1200 大于 1000,第二个 <source> 的条件都满足,浏览器就选择其作为 <img> 的源,因此会加载 cat-big.webp。

图片来源:https://webapplog.com/programmer-vs-software-engineer-vs-software-developer-vs-coder/

而在 iPhone 6 中,375 小于 500,第一个 <source> 满足条件,因此浏览器会加载 cat-small.jpg。

小结

以上就是响应式图片设计中关于分辨率切换艺术方向的实现方法,用到了 <picture> 与 <img>+srcset,这两者在使用上还有一些需要注意的地方:

  1. <picture> 中的元素要注意顺序,浏览器是按照顺序匹配的,因此顺序错误可能会导致选择了错误的图片。如果 <img> 不放在最后,那么其后面的 <source> 会失效,因为浏览器最多匹配到 <img> 就结束了。
  2. 由于两种方案都能代替原来的 src,因此不能通过 img.src 获取实际加载的图片地址,可以等图片加载完后通过 img.currentSrc获取真正的图片地址,例如:
    img.addEventListener('load'() => { console.log(img.currentSrc || img.src)})
  3. 避免使用 <source> 的sizes 属性,不要混用 <picture> 与 <img>,这会让情况变得复杂而且代码难以维护,例如:

    <picture>  <source media="(max-width: 600px)"          sizes="500px"          srcset="600.jpg 600w, 1200.jpg 1200w">  <source media="(max-width: 1200px)"          sizes="1000px"          srcset="1200.jpg 1200w, 2300.jpg 2300w">  <img src="800.jpg" srcset="600.jpg 1x, 1200.j" alt="my image"></picture>

可能你会觉得 <picture> 与 <img>+srcset 在功能上差不多,都是根据设备条件选择不同的源,但两者存在一些差别:

  1. <img>+srcset 是提供信息给浏览器,让越来越智能的浏览器作出决定,更轻量和简单
  2. <picture> 可以让你声明式地为浏览器提供不同的源,浏览器会严格按照 media 和 type 属性去匹配 <source>,所以需要开发者更细心地设计断点等条件。而且通过 type 属性,可以使用没有完全兼容的新格式图片,如具有更高压缩率的 WebP ,在支持 WebP 的浏览器中,可以节省更多流量,提高加载速度。

因此建议在分辨率切换这种更为简单的情况下,尽量使用 <img>+srcset 这一方案,而在艺术方向这种对显示要求更严格的情况下,应该选择使用 <picture>

 

图片的其他注意事项

响应式图片是响应式布局中的重要部分,需要考虑是否会带来性能问题。在开发过程中,还需要注意以下几个方面:

  1. 对图片进行懒加载
  2. 对于小的简单的图片,可以使用矢量图或者字体,保证在不同尺寸设备下都很清晰
  3. 对于尺寸小的图片,可以使用 Data URI 的方式,将图片转成 base64 内联在 CSS 或者 HTML 中,避免请求,但这样同样无法利用 HTTP 缓存,因此一般只对小于 1.5K 的图片做处理
  4. 挑选恰当的图片格式,PNG,JPEG 等,可以在 Android 下使用 WebP 格式
  5. 对图片进行压缩和优化
  6. 采用 CSS 和 CSS 动画代替一些简单的图片和动态图,如加载中 GIF 图

 

总结

本文从图片在不同设备中的显示问题出发,分析了为什么需要进行图片的响应式设计,然后进一步说明响应式图片的两大应用场景:分辨率切换和艺术方向。根据这两个方向的特点,通过代码和例子介绍了响应式图片的主要实现方法:<picture> 和 <img>+srcset,并比较了这两种方法的区别,最后提出了一些使用上的建议以及响应式图片开发中的注意事项。

以上内容在实际开发中不一定完全适用,开发者应根据具体情况作出更合适的选择,比如断点、图片大小采用的单位等,而且响应式图片还需与响应式文字、响应式布局等方面进行配合,才能实现更好的响应式效果,改善用户的体验。

未经允许不得转载:大自然的搬运工 » 响应式布局提高篇 – 图片正确的打开方式

赞 (0)

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址