如今开发一个网站不是响应式都不好意思拿出来,那么作为响应式中的重要一环「响应式图片」你又是如何解决的呢?
网站的平均加载已经到了近 2MB 并在不断地增加中,其中图片占据了绝大多数流量(63%)。可以肯定的是网页已经有了严重的大小问题,而图片就是罪魁祸首。虽然已经有很多种 措施 可以减少网页加载量,但或许更重要的步骤之一是确保响应式图片的加载方案。通过利用响应式图片解决方案,我们可以确保最佳的图片被加载,带宽不会被过大的图片所浪费。因此 W3C 定义了 picture 标签:基于检测客户端设备类型的可替换图片方案。具体是由 picture 这个标签去实现,也就是说我们现在有了一个基于标准的响应式图片解决方案可以用在实践中。
长话短说(人话)
- 断点(breakpoints)应该 取决于内容 而不是设备宽度 (CSS3 media query)
- 根据尺寸加载不同图片
- 计算图片像素密度并加到图片加载列表中
- 通过消除图片加载列表中太相近的值来让加载列表变得可维护
- 利用程序自动输出图片的不同大小
- 利用 img 标签的 srcset sizes 等属性输出同一张图片的不同路径/尺寸,以解决响应式图片的大小和像素密度的问题
- Picturefill 库能让我们现在就使用这些强大的特性
- 虽然 Picturefill 还有一些缺点 但这么做仍然利大于弊
定义
第一步就是定义所有响应式图片的尺寸和断点,这些信息在网页原型中就应该被精确的标示出来。重要的一点是 你的断点应该取决于你的内容 而不是不同设备的宽度。这么做是因为设备的参数是在不断变化的,流行的设备尺寸总是在变。通过让内容来决定断点位置,这将确保我们的界面在所有屏幕上响应而不是特殊的几个设备。
注意 当决定哪些图片应该被做成响应式时,要记住一点,大多数都是放在内容中的图片。例如在 HTML 中插入的图片而不是在 CSS 中的背景图片。
图片尺寸
首先将你的浏览器窗口放到最大(或者你规定的内容展示最大宽度),然后记录下此时你的内容宽度和你想要展示的图片宽度,通过浏览器的开发者工具或者类似的插件。
接下来缩放你的浏览器窗口直到你想要给图片设置宽度的下一个断点,再继续缩放直到你记录下所有的作用于图片宽度断点。当你完成的时候你应该记录下每张图片在不同大小下应该载入的宽度。
举例,仅仅是例子:
<span class="pun">[</span><span class="pln">max</span><span class="pun">-</span><span class="pln">width</span><span class="pun">]</span> <span class="pun">:</span> <span class="pun">[</span><span class="lit">1440</span><span class="pun">]</span> <span class="pun">[</span><span class="pln">breakpoint large</span><span class="pun">]</span> <span class="pun">:</span> <span class="pun">[</span><span class="lit">1120</span><span class="pun">]</span> <span class="pun">[</span><span class="pln">breakpoint medium</span><span class="pun">]</span> <span class="pun">:</span> <span class="pun">[</span><span class="lit">800</span><span class="pun">]</span> <span class="pun">[</span><span class="pln">reakpoint small</span><span class="pun">]</span> <span class="pun">:</span> <span class="pun">[</span><span class="lit">400</span><span class="pun">]</span>
注意 关于决定断点需要注意的是:断点越多,代码越难维护。除此之外大量的断点会使 CSS 变得臃肿。所以尽量在保证效果的情况下保持最少的断点。
高分辨率
下一步是根据你想要支持的分辨率 对图片宽度进行计算。决定要支持那些分辨率是很困难的,因为有 太多的不同的分辨率,并且每支持一种分辨率你需要计算宽度并放在你的图片加载列表里。所以需要做的是根据实际情况和你的用户群体去选择支持不同的分辨率。
如果你已经决定了需要支持那些高分辨率,那图片加载列表会像下面这样:
<span class="pun">[</span><span class="pln">max</span><span class="pun">-</span><span class="pln">width</span><span class="pun">]</span> <span class="pun">:</span> <span class="pun">[</span><span class="pln">image width</span><span class="pun">],</span> <span class="pun">[</span><span class="pln">image width x1</span><span class="pun">.</span><span class="lit">5</span><span class="pun">],</span> <span class="pun">[</span><span class="pln">image width x2</span><span class="pun">]</span> <span class="pun">[</span><span class="pln">breakpoint large</span><span class="pun">]</span> <span class="pun">:</span> <span class="pun">[</span><span class="pln">image width</span><span class="pun">],</span> <span class="pun">[</span><span class="pln">image width x1</span><span class="pun">.</span><span class="lit">5</span><span class="pun">],</span> <span class="pun">[</span><span class="pln">image width x2</span><span class="pun">]</span> <span class="pun">[</span><span class="pln">breakpoint medium</span><span class="pun">]</span> <span class="pun">:</span> <span class="pun">[</span><span class="pln">image width</span><span class="pun">],</span> <span class="pun">[</span><span class="pln">image width x1</span><span class="pun">.</span><span class="lit">5</span><span class="pun">],</span> <span class="pun">[</span><span class="pln">image width x2</span><span class="pun">]</span> <span class="pun">[</span><span class="pln">breakpoint small</span><span class="pun">]</span> <span class="pun">:</span> <span class="pun">[</span><span class="pln">image width</span><span class="pun">],</span> <span class="pun">[</span><span class="pln">image width x1</span><span class="pun">.</span><span class="lit">5</span><span class="pun">],</span> <span class="pun">[</span><span class="pln">image width x2</span><span class="pun">]</span>
整合
如你所见,我们的图片列表数量会随着支持不同的分辨率和断点而变长。通过改变整合列表项让列表更清晰可控是很有必要的。例如任何相近或者不超过 200 像素差距的值。将几个相近的值整合为一个值将有助于构建更清晰的列表:
<span class="pun">(</span><span class="pln">min</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="lit">1280px</span><span class="pun">)</span> <span class="pun">:</span> <span class="lit">1040px</span><span class="pun">,</span> <span class="lit">1560px</span><span class="pun">,</span> <span class="lit">2080px</span> <span class="pun">(</span><span class="pln">min</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="lit">1120px</span><span class="pun">)</span> <span class="pun">:</span> <span class="lit">924px</span><span class="pun">,</span> <span class="lit">1386px</span><span class="pun">,</span> <span class="lit">1848px</span> <span class="pun">(</span><span class="pln">min</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="lit">800px</span><span class="pun">)</span> <span class="pun">:</span> <span class="lit">800px</span><span class="pun">,</span> <span class="lit">1200px</span><span class="pun">,</span> <span class="lit">1600px</span> <span class="pun">(</span><span class="pln">min</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="lit">400px</span><span class="pun">)</span> <span class="pun">:</span> <span class="lit">400px</span><span class="pun">,</span> <span class="lit">600px</span><span class="pun">,</span> <span class="lit">800px</span>
注意 这里用像素来做断点值只是为了好对应图片宽度,实际上你的 断点应该使用相对单位(em/rem not px)
输出
现在我们有了一个慎重考虑的图片宽度列表,下一步则要将每个图片导出为以「断点名」+「像素密度倍数」为名的文件。例如我将最大的断点称为 “large” 并且图片像素密度倍数为两倍,那么我的文件名则为 ‘image_large@2x.jpg’。我倾向于在 Photoshop 中保存图片为 70% 压缩,因为 70% 压缩能确保达到最优的图片大小并且不会损失过多的清晰度(这取决于不同图片,目的是在保证清晰度的情况下尽量减小文件大小)。如果你倾向于保存为 JPEG 格式,那确保通过勾选 progressive(渐进) 来让图片从模糊到清晰的加载效果。
译者注 PS/AI 中保存图片请使用「存储为 web 格式/save for web」,快捷键 ctrl/command + shift + alt + s。PNG 格式请勾选 交错/interlaced,JPEG 格式请勾选 渐进/progressive。
脚本
通过提前写好动作脚本(宏)可以让你批量自动化导出你想要的图片大小。根据你选择的设计软件,自动化的脚本可以很容易的设置使用。不幸的是你仍然需要手动重命名每张图片为 ‘xxx_large@2x.png’ 这样的命名格式。如果你使用 Photoshop 这里有一个 指南 可以帮助你编写你自己的脚本批处理。或者你也可以用这个 写好的 PS 脚本。
构建工具
另外一个自动化导出图片的方法是使用构建工具,我选择使用 Gulp。Gulp 是一个基于 Javascript 流式思想的构建工具,这使得构建复杂任务的编写更加简单。这里有各种任务插件,包括调整图片大小、文件重命名。只需要编写小段配置,你就可以完全自动化工作流中的某些部分,并且在使用的时候你无须再考虑。使用构建工具的另一个好处是你可以将你的任务链式的串联起来并且可调整顺序。
优化
最后,你需要注意的是在将你的图片放进网页前进行优化。这将确保图片数据多余的数据被删除,将有效的减小文件的大小。和处理图片或缩放图片一样,有很多种方法可以完成这个任务:你可以使用软件或者终端命令手动优化图片,或者你也可以使用构建工具自动完成这项任务。我喜欢的 JPGs 图片压缩软件是 imageOptim PNG 是 imageAlpha,还有一大堆软件你可以选择。
另外你也可以使用强大的构建工具,好处你不需要记得压缩图片,你只需要在你的每个项目里区配置工具即可。我选择用 Gulp 的插件 (imagemin)[https://www.npmjs.com/package/gulp-imagemin] 来做这件事情,它也能压缩 SVG 和 GIF 文件。
实现
最终的步骤是在网页中实现响应式图片。这种方法即所谓的 分辨率切换,因为我们只是切换了同一张图片在不同尺寸和分辨率下的源文件地址,以达到在不同的尺寸和像素密度下达到响应的目的。所以我们会使用 picture 标准的一部分 srcset 和 sizes 属性。这些属性继承了 <img> <source> 标签,提供了一个可供浏览器选择最佳图片的图片地址列表。我们实际上提供给浏览器的是我们所知道的信息,而具体会加载那个源则是未知的。这取决于用户设备的带宽、像素密度等。
srcset 属性
让我们从 srcset 属性开始,首先我们会提供一个 src 属性给那些不支持 srcset 属性的浏览器。中等大小和分辨率就足够了(不支持 srcset 属性的浏览器用户电脑的尺寸和分辨率也不会大)。接下来通过 srcset 指定给 <img> 标签所有图片源的信息。然后使用逗号分隔列出一个从小到大的图片源列表。每个图片源后可以跟w描述符或者x描述符。最后的结果看起来像是这样:
<span class="tag"><img</span> <span class="atn">src</span><span class="pun">=</span><span class="atv">"image_medium.jpg"</span> <span class="atn">srcset</span><span class="pun">=</span><span class="atv">"image_small.jpg 400w, image_small@1.5x.jpg 600w, image_medium.jpg 800w, image_xlarge.jpg 1040w, image_large@1.5x.jpg 1386w, image_medium@2x.jpg 1600w, image_large@2x.jpg 1848w, image_xlarge@2x.jpg 2080w"</span> <span class="atn">alt</span><span class="pun">=</span><span class="atv">"Image description"</span> <span class="tag">/></span>
现在我们有了一个可供选择源的图片标签,浏览器可以根据选择采用最佳的源,同时我们还有一个回退方案填写在 src 属性中。如果浏览器支持 srcset 则会下载最佳的图像,否则直接下载 src 属性内的图像。因此带宽浪费和页面大小冗余会被降到最低。
需要注意的是 srcset 只是描述了一个 <img> 有哪些可用的源,后面的 w 描述符并不是说某个宽度下就一定会加载这个源。具体的情况还是取决于用户的浏览器环境、带宽等 同时对于同一源,只能标示一个符号w/x,不能同时标记两者,也建议不要在列表中混用 w/x,混用将会导致计算过于复杂。
sizes 属性
我们也可以通过设置 sizes 属性来帮助浏览器选择不同的图片源,通过 size 属性浏览器知道我们的图片在不同的 viewport 下占了多大,也就是图片相对于 viewport 的比例。sizes 属性并不是必填的,没有sizes 属性 srcset 仍然有效。但是如果 srcset 属性没有那么 sizes 属性将不会生效。
如果没有 sizes 属性,那么 srcset 则被认为不论图片占多宽,都始终采用和 viewport 相同宽度的源(排除分辨率的影响)。建议 sizes 应该配合 srcset 一起使用:
<span class="tag"><img</span> <span class="atn">src</span><span class="pun">=</span><span class="atv">"image_medium.jpg"</span> <span class="atn">sizes</span> <span class="pun">=</span><span class="atv">"(min-width:1120px) 924px, (min-width:1280px) 1040px, 100vw"</span> <span class="atn">srcset</span><span class="pun">=</span><span class="atv">"image_small.jpg 400w, image_small@1.5x.jpg 600w, image_medium.jpg 800w, image_xlarge.jpg 1040w, image_large@1.5x.jpg 1386w, image_medium@2x.jpg 1600w, image_large@2x.jpg 1848w, image_xlarge@2x.jpg 2080w"</span> <span class="atn">alt</span><span class="pun">=</span><span class="atv">"Image description"</span> <span class="tag">/></span>
上面的代码告诉浏览器,在视窗宽度小于 1120px 的时候这个图片要加载 924px 宽度的,在视窗宽度小于 1280px 大于 1120px 的时候要加载 1040px 的图片,视窗大于 1280px 的加载和视窗宽度相同的图片。
浏览器将使用这些属性来进一步为用户选择合适的图片源,你可以选择增加更多的断点和不同宽度下图片的加载列表(但这会显著的增加布局内的信息),或者你也可以保持相对简单。关键是现在浏览器知道了关于图片的更多信息,它将在不同的情况下选择最佳的图片源。
艺术指导(ART-DIRECTION USE CASE)
大多数情况下 srcset 和 sizes 都已经够用了。但是仍然有时候你需要根据不同的尺寸修改图片的内容。例如使用类似于 srcset 和 sizes 标准的 <picture> 标签。关于 <picture> 的更多信息可以了解 Jason Grigsby’s article (这篇文章其实说的大多数情况下不要用 picture,个人认为其实不同的图片内容也可依赖 srcset,picture 过于复杂。并且兼容性上 picture 不如 srcset )。
浏览器兼容
srcset 和 sizes的浏览器兼容性 已经获得了更多的支持,但对于大多数项目你需要更多的考虑兼容。幸运的是 Picturefill 是一个很棒的支持跨浏览器的兼容库,它支持 <picture> 和 srcset 以及 sizes 特性。这个兼容库允许你使用推荐的 <picture> 语法,因此可以在浏览器兼容性没有问题的时候移除该库。虽然 Picturefill 还有一些缺点,但这么做仍然利大于弊。
注意 在使用 picturefill 库的时候,作者推荐抛弃使用 src 标签,因为这将 导致图片的重复下载。
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/259285.html