现代图像服务方式(译)

HTTPArchive 发现至少 70% 的网站将图像作为最突出的元素,但只有 34% 的网站使用 <img srcset> 来创建响应式和高性能图像(使用 <picture> 的网站更少)。为了改变这一点,下面将引导完成使用响应式图像技术,并介绍一种新颖的技术,让使用 <img> 标签(几乎)像 <picture> 标签一样,从而使现有代码升级变得更加容易。从而减少升级工作量,帮助在2023年提升图片体验。

为什么我们需要响应式图像

一般来说,高质量的图像会带来更好的用户体验。网络上有无数关于这个主题的文章,关于人类对图像的感知以及图像质量的研究几乎是无穷无尽的。

(…) 高质量和专业拍摄的照片通常能够持续提高用户参与度 (…)

–幅画值得一千字吗?实证研究,Yiyi Li 和 Ying Xie,SAGE 期刊

让我们退后一步看。响应式图片除了高质量之外需要满足哪些条件?

在大屏幕和小屏幕(例如桌面和移动设备)上应该能够显示不同尺寸的图像。手机屏幕空间较小,因此我们需要更加注意可见性,尤其在与营销团队合作时,他们通常会针对不同设备提供不同的图像。

我们还应该考虑当前的缩放级别,因为缩放比你想象的更常见(例如出于辅助功能的考虑)。当用户缩放时,设备像素比(DPR)会发生变化,因此显示更高分辨率的图像是有其意义的。

不要忽视性能问题,较低的图像质量会相应的减少文件的大小(文件格式也会影响这一点),这对于页面加载速度和Web Vitals至关重要。通过加载较小的文件,可以减少网络数据的使用,从而可以提升那些拥有较慢互联网、老旧设备并且需要按下载的每兆字节付费的国家创造良好的体验。关于文件格式和质量的介绍稍后会提到。

这将给我们带来以下的核对清单:

☑️️ 根据视口大小提供不同尺寸的图像(例如为桌面和移动设备提供不同的图像)

☑️️ 根据视口大小提供不同质量的图像

☑️️ 根据设备像素比(DPR)/缩放级别提供不同质量的图像

☑️️ 可选:提供不同的文件格式(WebP,AVIF等)
 
接下来让我们了解如何满足这些要求。

适应所有情况:使用 标签

杰克·阿奇博尔德(Jake Archibald)2021年在他的文章《为高密度显示器提供图像》中提出了一个方法。他使用 标签来检查所有要求(包括可选的),接下来我将对其进行解释:

<picture>
  <source
    // `media` contains a CSS media query (MQ) that is used to control which
    // specific source to render (first true `<source>` wins)
    media="(-webkit-min-device-pixel-ratio: 1.5)"
    // `srcset` contains the path to an image and an 'intrinsic width descriptor',
    // corresponding to the original image width on your device
    srcset="2x-800.jpg 800w, 2x-1200.jpg 1200w, 2x-1598.jpg 1598w"
    // `sizes` consists of a CSS MQ condition and the width of the slot
    // You can also use a viewport width (`vw`)
    sizes="
      (min-width: 1066px) 743px,
      (min-width: 800px) calc(75vw-57px),
      100vw">
  <img src="1x.jpg" >
</picture>

在这个示例中,对于旧版本的浏览器和低于1.5x DPR(设备像素比)的屏幕,会加载1x.jpg图像。对于其他屏幕,浏览器根据视口宽度的不同来区分,所以现代手机会加载2x-800.jpg,桌面设备会加载2x-1598.jpg。

<picture> 标签由任意数量的 <source> 标签和一个 <img> 标签组成。后者的工作方式类似于独立的 <img> 标签,您可以添加 alt 属性、loading=”lazy” 等。

为了确定渲染哪个图像,<picture> 标签会告诉浏览器需要加载哪些内容(即由 <source> 标签定义的内容),有点像在说:“我负责确定何时加载哪个图像,请按照我的指示进行”。

现在我们可以…

☑️️ 根据视口大小提供不同尺寸的图像

☑️️ 根据视口大小提供不同质量的图像

☑️️ 根据设备像素比(DPR)/缩放级别提供不同质量的图像

☑️️ 可选:提供不同的文件格式

正如我们所看到的,<picture> 标签是明确告诉浏览器在哪种情况下执行什么操作是最合适的。如果你是新手,最好使用 <picture> 标签来实现响应式、高性能和清晰的图像。

然而,进一步考虑:切换到 <picture> 也有一些缺点 – 从普通的 <img> 标签切换过来需要对现有代码进行大量重构,需要花费较大的时间和精力。在接下来的章节中,我们将保持使用传统的 <img> 标签以避免上述问题的出现。

完美实现:<img srcset> 标签

另一种替代<picture>元素的方法是使用<img srcset>。对于这两个元素,都会向浏览器提供图像及其相关信息。然而,这两个标签之间的微小差异在于:使用<img>时,会将控制权交给浏览器 – 实质上是告诉浏览器“你负责确定加载哪个图像最佳”。因为<img>常用,大多数网站倾向于使用以下方法来显示响应式图像。

像素密度描述符

首先,让我们来看一下’像素密度描述符’,其中单位为’x’,代表每像素点的点数(dppx),它反映了设备像素比(DPR)。

<img srcset="medium.jpg, large.jpg 2x, ultra.jpg 3x" ... />

在这种情况下,标准显示器的浏览器会加载’medium.jpg’,在更高分辨率的显示屏上(例如视网膜显示屏、iPhone、任何现代Android设备、4K显示器等),我们会加载’large.jpg’或’ultra.jpg’。

像素密度描述符允许我们使用一个小技巧。Filament Group在2012年发表了一篇文章,介绍了将高DPR屏幕图像质量减半的想法。保留图像的大小是渲染大小的两倍(例如内在大小400×400 -> 渲染大小200×200),同时将质量降低到50%。由于浏览器在显示图像时进行的图像缩放,图像看起来很清晰,同时文件大小更小。

这种方法满足哪些条件?

❌ 根据视口大小提供不同尺寸的图像

❌ 根据视口大小提供不同质量的图像

☑️️ 根据设备像素比(DPR)/缩放级别提供不同质量的图像

❌ 可选:提供不同的文件格式

即使您将质量降低到50%,这种方法意味着您的手机会加载比必要的更大的图像,因为您无法控制尺寸,因此无法根据不同的视口宽度提供不同的图像。 

在移动设备上,长时间下载时间会降低网站性能,浪费用户的数据流量,并通过解码+渲染更大的图像来占用CPU资源(这也增加了电池的使用量)。 

我们能做得更好吗?是的。

您还可以在背景图像中使用CSSimage-set来使用像素密度描述符,但它们有自己的性能缺点,正如Harry Roberts所解释的那样。因此,我不建议使用它们。

宽度描述符

接下来是目前最流行的使用响应式转存失败,建议直接上传图片文件标签的方式。以下是最好的解释:

srcsetsizes 属性的工作方式与它们在 <source> 标签中的工作方式完全相同,唯一的区别是没有可用的 media 属性。在上面的代码片段中,为了确定渲染哪个图像,浏览器使用第一个计算为 true 的 sizes 媒体查询。假设它是 (min-width: 64em) 750px – 750px 告诉浏览器“请渲染 (最小) 宽度描述符为 750w”,因此浏览器加载 medium.jpg。旧版本的浏览器会回退到 src

宽度描述符还适应设备的 DPR,Chris Coyier 在 2014 年就在 CSS-Tricks 博客上发表了相关文章。由于浏览器可能自那时起有所更改,所以让我们看一下今天不同浏览器的行为:

<img src="small.jpg" srcset="medium.jpg 1000w, large.jpg 2000w" ... />

左上角显示已下载图像的分辨率和质量。

根据规范,浏览器将使用 <img> 元素的 ‘source size’(源大小)除以宽度描述符,以确定每个图像的密度,然后将其与设备的 DPR(包括当前缩放级别)进行比较。’source size’ 由 sizes 属性定义,如果省略,则默认为 100vw(当前屏幕宽度)。

Firefox 和 Safari 选择大于当前 DPR 的密度(如果不可能,则选择最高的密度)来确定输出图像。Chromium 对于 DPR <= 1x 也是如此,但对于 DPR > 1x,基于 Chromium 的浏览器会检查两个值之间的几何平均值来确定要显示的图像。

// imagine this to be a very simplified loop
if (next_density < DPR) { continue; /* skip to next srcset entry */ }
if ((DPR <= 1 && DPR > current_density)
  || DPR >= geometric_mean(current_density, next_density))
  return next;
return current;

作为经验法则,Chromium 浏览器在 DPR 大于 1x 时选择最接近(在 srcset 中描述的宽度)视口宽度的图像。 

让我们来看一个例子。对于视口宽度为 360px,浏览器进行以下计算来得到密度:

1000w / 360px = 2.8x

2000w / 360px = 5.6x

在 DPR 为 3x 的情况下:

  • Firefox 和 Safari 选择 slot large.jpg 2000w 中的密度 5.6x
  • Chrome:
    • 计算几何平均值:√2.8x * 5.6x = 4x
    • 3x >= 4x 为假,所以选择 medium.jpg 1000w

总结一下,由于宽度描述符实际上也是像素密度描述符,我们可以看到为什么宽度描述符是更流行的使用 srcset 的方式。然而,这种行为带来了一些令人恐慌的问题:

首先,由于浏览器实现的差异,规范将决定最佳图像的权利留给用户代理。您可以使用实验场检查不同的 DPR 和视口宽度,或在另一个设备/浏览器上打开此帖子,并查看图像的左上角。您会注意到,有些浏览器加载相同的图像,在的手机上显示得更小。因此,使用隐式机制会导致无法明确地针对视口大小进行定位。其次,这种方法会导致性能问题(WhatWG 也承认了这一点):

根据纯粹的错误 sizes 属性,我们估计有 33% 的桌面页面加载了超过 83 KB 的额外图像数据。也就是说:在 srcset 中有更好、更小的资源可供选择,但由于 sizes 属性的错误,浏览器没有选择它。此外,由于错误的 sizes 属性,有 10% 的桌面页面加载了超过 0.5 MB 的额外图像数据!

— 《Web Almanac,HTTP Archive》 

HTTPArchive 还提到有 10% 的网站在移动设备上多使用了 283 KB 的数据。83 KB 相当于在同一页上加载两次完整的 React 库。这些统计数据甚至还不包括由于没有优化文件大小而出现的额外数据开销。 

总的来说,这种方法满足哪些条件?

☑️️ 根据视口大小提供不同尺寸的图像

☑️️ 根据视口大小提供不同质量的图像

☑️️ 根据设备像素比(DPR)/缩放级别提供不同质量的图像(隐式地)

❌ 可选:提供不同的文件格式 

我们能做得更好吗?是的。

我们的目标是采用一种方法,可以根据视口大小和设备像素比(DPR)/缩放级别提供不同尺寸和质量的图像,以实现性能的优化。 

最佳融合:’布尔’ 转存失败,建议直接上传图片文件 标签

话不多说,接下来让我介绍一个混合式 <img> 标签,它结合了前面章节的经验。这里我们有一个 <img> 标签,它结合了 sizessrcset 的宽度描述符以及隐式的像素密度计算,使其具有类似于 <picture> 元素的功能:

<img src="1x.jpg"
   sizes="
    (max-width: 480px) and (-webkit-device-pixel-ratio: 1)       1px,
    (min-width: 481px) and (-webkit-device-pixel-ratio: 1)       2px,
    (max-width: 480px) and (-webkit-min-device-pixel-ratio: 2)   3px,
    (min-width: 481px) and (-webkit-min-device-pixel-ratio: 2)  16px"
  srcset="

    medium-1x-q75.jpg   1w,
    large-1x-q75.jpg    2w,
    medium-2x-q35.jpg  15w,
    large-2x-q50.jpg   16w"
/>

这次,您应该能够看到每种设备类型的差异。当您放大浏览器时,会实时更新。

不要让可爱的熊猫分散您对混合式图片标签的注意力。我将在一分钟内解释这些数字,以及为什么第三个 sizes 条目与第三个 srcset 条目不匹配。

解释

通过结合 sizessrcset(宽度描述符),我们重新获得了对浏览器行为的控制。正如之前提到的,宽度描述符隐式地工作,因此我们引入了一个特别设计的 sizes 属性,以定位到单个 DPR,帮助我们再次明确地控制图像加载。我们精心设计的 <img> 标签现在的行为就像一个 <picture> 标签,无需额外的标签。我们可以有条件地加载不同尺寸和不同质量的图像。

较少改变 DOM 结构可以大大减少开发所需的时间,因为我们可以更少地花时间重写或重新排序,这在大型代码库中“热修复”模糊图像时,是非常实用的。 

sizes 属性

正如之前解释的,sizes 允许我们使用 CSS 媒体查询,这意味着我们不限于使用 min-max-width。此外,像 Andrea Verlicchi 的文章“将图像保持为 2x”,以及 Cloudinary 对视觉敏锐度的研究,为我们解释了为什么我们不需要区分 2.5x、3x,甚至更高分辨率的设备。

对于属性中的条件,我们使用以下方式:

  1. 使用 480px 作为断点来区分“手机”和“其他设备”这两个类别
  2. 使用 (-webkit-device-pixel-ratio) 和 (-webkit-min-device-pixel-ratio) 条件来区分 1x DPR 和 >= 2x DPR 屏幕 

有条件的 -webkit-(min)-device-pixel-ratio 在非 WebKit 浏览器中也得到了很好的支持。如果您只支持 Safari 16 及更高版本,您可以用 (resolution: 1dppx) 和 (min-resolution: 2dppx)(caniuse)来替代带前缀的条件。

现在让我们来看看这个技巧,如果我们记住浏览器选择第一个真值条件,我们可以设计类似 if-else 的语句:

If screenwidth       <= 480px && DPR == 1: Load slot ‘1’

Else-If screenwidth >= 481px && DPR == 1: Load slot ‘2’

Else-if screenwidth <= 480px && DPR >= 2: Load slot ‘3’

Else-if screenwidth >= 481px && DPR >= 2: Load slot ‘16’

现在,每个查询的值不再是我们要显示的图像宽度,而是我们要定位的 srcset 插槽。换句话说,我们消除了“图像宽度”和“宽度描述符”之间的(近似)关联,而是使用数字来定位 srcset 的特定插槽。使用规范的技术术语,我们将 <img> 标签的“source size”设置为所选插槽。

瞧,现在我们有“布尔”条件来区分何时加载哪个图像!

srcset 属性

正如前面提到的,我们现在使用 sizes 中的像素(’px’)来设置“source size”,以便定位到 srcset 的特定插槽。结合在“宽度描述符”章节中提到的隐式像素密度计算的知识,我们创建了一个 srcset 映射,使浏览器加载来自我们真实 sizes 条件的图像,该条件是下一个较大的插槽(或最低几何平均值1)。

由于这有点难以理解,让我们看一个 DPR 为 3x、屏幕宽度为 360px 的示例:

  • 对于 sizes 属性,(max-width: 480px) 和 (-webkit-min-device-pixel-ratio: 2) 3px 是第一个 MQ 评估为 true 的条件,告诉浏览器将“source size”设置为 3px。
  • 接下来,浏览器确定正确的 srcset 插槽:
    • 像素密度:
      • 1w / 3px = 0.33x,2w / 3px = 0.67x
      • 15w / 3px = 5x,16w / 3px = 5.33x
    • 所有浏览器选择的插槽都是 medium-2x-q35.jpg 15w,原因如下:
      • 下一个较大的像素密度(Firefox,Safari):来自插槽 15w 的 5x
      • Chromium:
        • 计算几何平均值:√5x * 5.33x = 5.16x
        • 3x >= 5.16x 为假,因此选择插槽 15w。

将来可能会出现设备的 DPR 为 5x,为了防止模糊图像在 2030 年(猜测)重新出现,我们需要确保将两个 2x DPR 大小值都乘以 5,以便 Safari 和 Firefox 选择正确的 srcset 值(3*5 => 下一个较大的是 15w)。这种方法只能在屏幕大于 5x 的情况下有效,但由于我们已经接近了人类感知的极限,我认为过去几年不断增加的 DPR 支持可能会被限制,甚至 5x 也只是一种预防措施。

sizessrcset 中的最后一个插槽基本上是我们 if-else 链中的 else,这意味着我们可以简单地添加 +1 来创建最后一个插槽,供“其他设备” && DPR >= 2 类别使用。

最后但并非最不重要的是,我们使用 src 属性设置了默认值,以供那些不识别我们任何操作的浏览器使用。如果没有任何媒体查询匹配,那么默认值就是 100vw。

“布尔”<img>标签的结论

通过使用这种方法,我们可以安全地确定哪个浏览器或设备渲染了哪个图像。不再有意外发生。虽然 比这种方法更明确,但使用“布尔”<img>标签比引入额外的 HTML 标签进行重构更省力,而引入额外的 HTML 标签还需要更新涉及 JavaScript 事件和 CSS 的依赖项。相比之下,添加或更新 srcset 和 sizes 这两个属性很容易。

现在我们可以…

☑️️ 根据视口大小提供不同尺寸的图像

☑️️ 根据视口大小提供不同质量的图像

☑️️ 明确地根据设备像素比(DPR)/缩放级别提供不同质量的图像 

如果你将其自动化(例如使用一个组件/库来自动创建这个过程),你将不再需要自己手动设计它。以下数学公式可以在任何地方实现,其中 N、M 是每个类别的图像宽度,A 是断点数:

<img
  sizes="
    (max-width: Apx) and (resolution: 1dppx) Npx,
    (min-width: (A+1)px) and (resolution: 1dppx) Mpx,
    (max-width: Apx) and (min-resolution: 2dppx) (M+1)px,
    (min-width: (A+1)px) and (min-resolution: 2dppx) (((M+1)*5)+1)px"
  srcset="

    low-dpr-xs.jpg Nw,
    low-dpr-xl.jpg Mw,
    high-dpr-xs.jpg ((M+1)*5)w,
    high-dpr-xl.jpg (((M+1)*5)+1)w"
  src="fallback.jpg"
  alt="don't forget the alt attribute"
/>

附录:文件格式与质量

现在我们已经勾选了所有强制性的复选框,只剩下一个可选的复选框,即绕过在 <source> 标签中区分 PNG 和 WebP 文件格式的问题。web.dev 文章在此列出了所有选项。

简而言之:图像优化代理通过检查请求的 HTTP 标头自动在同一 URL 上交付不同的文件格式,这意味着我们不需要在 HTML 中进行处理。

图像代理还给我们带来了另一个优势:这样的代理还允许我们定义图像的质量和 DPR。

在我的现任雇主,Jochen Schweizer mydays 集团,我们测试了不同 JPEG 图像质量的感知效果,并发现在具有 2x DPR 屏幕的智能手机上,50% 和 35% 质量之间没有明显的区别。对于一般的 1x 屏幕,75% 的质量对我们来说效果不错。但是效果因人而异,因此您应该针对您的网站进行 A/B 测试。

现在,我们差不多可以:

☑️️ 提供不同的文件格式

对于那些想要在家里尝试的人 ‒ 对于 Apache web 服务器,您可以通过重新编写请求来轻松实现:

附录:缓存与调试

虽然本文中的数学计算在缩放时会更新,但基于Chromium的浏览器被优化为使用更少的网络流量,因为如果高分辨率图像在缓存中存在,则img标签将不会加载低分辨率图像,即使您在DevTools中关闭缓存。这意味着如果您将手机旋转到横向模式,高分辨率图像将保持不变。因此,请确保在禁用缓存时刷新页面以测试本文。

最后但并非不重要的是,关于调试DPR的快速提示 – 您的好朋友DevTools将使您的生活比与不同设备进行调试更轻松。以下是切换DPR的方法:

启用设备仿真,点击三个点,并选择“添加设备像素比”。

通过在工具栏上点击“DPR:”来在不同的DPR之间切换。

脚注和评论

感谢Andrea VerlicchiBarry PollardIvan Akulov对本文提供的宝贵反馈。

  1. 请参阅此Twitter帖子Blink代码
  2. WhatWG的问题自2019年以来一直开放,Chromium也有一个开放的问题,您可以为其加星标,该问题建议将用于计算的DPR上限设为2.2x。
  3. WebP团队还提供了一个编解码器比较,您可以自行比较WebP2与JPEG XL等。

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYfscIKi' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片