规范性语法

<picture> 元素本身不呈现任何内容,而是充当内部 <img> 元素的决策引擎,告诉它要呈现什么。<picture> 遵循了 <audio><video> 元素已设定的先例:一个包装元素,其中包含各个 <source> 元素。

<picture>
   <source …>
   <source …>
    <img …>
</picture …>

内部 <img> 还为您提供了针对不支持响应式图片的旧版浏览器的可靠回退模式:如果用户的浏览器无法识别 <picture> 元素,则会忽略它。<source> 元素也会被丢弃,因为浏览器要么根本无法识别它们,要么在没有 <video><audio> 父元素的情况下对它们没有有意义的上下文。但是,任何浏览器都将识别内部 <img> 元素,并且将按预期呈现其 src 中指定的来源。

使用 <picture> 的“艺术指导”图片

根据图片在页面中的大小更改图片的内容或宽高比通常称为“艺术指导”响应式图片。srcsetsizes 旨在以不可见的方式工作,随着用户浏览器的指示无缝地切换来源。但是,有时您希望跨断点更改来源,以便更好地突出显示内容,就像您调整页面布局一样。例如:具有小中心焦点的全宽标题图片可能在大型视口上效果良好

A header width image of a periwinkle flower surrounded by leaves and stems, being visited by a honeybee.

但是,当缩小以适应小型视口时,图片的中心焦点可能会丢失

A header width image of a periwinkle flower, scaled down. The honeybee is barely visible.

这些图片来源的主题是相同的,但为了在视觉上更好地关注该主题,您需要图像来源的比例在断点之间发生变化。例如,更紧密地缩放图像的中心,并裁剪掉边缘的一些细节

A zoomed in crop of the periwinkle flower.

这种“裁剪”可以通过 CSS 实现,但会让用户请求构成该图片的所有数据,即使他们最终可能永远看不到它。

每个 source 元素都具有定义选择该 source 的条件的属性:media,它接受媒体查询,以及 type,它接受媒体类型(以前称为“MIME 类型”)。源顺序中第一个与用户当前浏览上下文匹配的 <source> 被选中,并且该 sourcesrcset 属性的内容将用于确定该上下文的正确候选项。在此示例中,第一个具有与用户视口大小匹配的 media 属性的 source 将是被选中的那个

<picture>
  <source media="(min-width: 1200px)" srcset="wide-crop.jpg">
  <img src="close-crop.jpg" alt="…">
</picture>

您应始终将内部 img 放在顺序的最后 - 如果没有 source 元素匹配其 mediatype 条件,则图像将充当“默认”来源。如果您使用 min-width 媒体查询,您希望首先拥有最大的来源,如前面的代码所示。当使用 max-width 媒体查询时,您应首先放置最小的来源。

<picture>
   <source media="(max-width: 400px)" srcset="mid-bp.jpg">
   <source media="(max-width: 800px)" srcset="high-bp.jpg">
   <img src="highest-bp.jpg" alt="…">
</picture>

当基于您指定的条件选择来源时,source 上的 srcset 属性将传递给 <img>,就像它在 <img> 本身中定义一样 - 这意味着您可以自由使用 sizes 来优化艺术指导的图像来源。

<picture>
   <source media="(min-width: 800px)" srcset="high-bp-1600.jpg 1600w, high-bp-1000.jpg 1000w">
   <source srcset="lower-bp-1200.jpg 1200w, lower-bp-800.jpg 800w">
   <img src="fallback.jpg" alt="…" sizes="calc(100vw - 2em)">
</picture>

当然,比例可能因所选 <source> 元素而异的图像会引发性能问题:<img> 仅支持单个 widthheight 属性,但 省略这些属性可能会导致用户体验明显变差。为了解决这个问题,HTML 规范中 相对较新支持良好的补充允许在 <source> 元素上使用 heightwidth 属性。这些属性与在 <img> 上一样有助于减少布局偏移,在您的布局中为选定的任何 <source> 元素保留适当的空间。

<picture>
   <source
      media="(min-width: 800px)"
      srcset="high-bp-1600.jpg 1600w, high-bp-1000.jpg 1000w"
      width="1600"
      height="800">
   <img src="fallback.jpg"
      srcset="lower-bp-1200.jpg 1200w, lower-bp-800.jpg 800w"
      sizes="calc(100vw - 2em)"
      width="1200"
      height="750"
      alt="…">
</picture>

重要的是要注意,艺术指导可以用于不仅仅基于视口大小的决策 - 并且应该如此,因为大多数这些情况可以通过 srcset/sizes 更有效地处理。例如,选择更适合用户偏好指示的配色方案的图像来源

<picture>
   <source media="(prefers-color-scheme: dark)" srcset="hero-dark.jpg">
   <img srcset="hero-light.jpg">
</picture>

type 属性

type 属性允许您使用 <picture> 元素的单请求决策引擎,仅向支持它们的浏览器提供图像格式。

正如您在图片格式和压缩 中了解到的,浏览器无法解析的编码甚至无法识别为图片数据。

在引入 <picture> 元素之前,用于提供新图片格式的最可行的前端解决方案要求浏览器请求并尝试解析图片文件,然后再确定是否丢弃它并加载回退。一个常见的例子是类似这样的脚本

   <img src="image.webp"
    data-fallback="image.jpg"
    onerror="this.src=this.getAttribute('data-fallback'); this.onerror=null;"
    alt="...">

使用这种模式,仍然会在每个浏览器中发出对 image.webp 的请求 - 这意味着对于不支持 WebP 的浏览器来说,传输是浪费的。然后,无法解析 WebP 编码的浏览器会抛出 onerror 事件,并将 data-fallback 值交换到 src 中。这是一个浪费的解决方案,但同样,像这样的方法是前端可用的唯一选择。请记住,浏览器在任何自定义脚本有机会运行甚至解析之前就开始请求图片 - 因此我们无法抢占此过程。

<picture> 元素专门设计用于避免这些冗余请求。虽然浏览器仍然无法识别它不支持的格式而无需请求它,但 type 属性会预先警告浏览器有关来源编码,以便它可以决定是否发出请求。

type 属性中,您提供每个 <source>srcset 属性中指定的图像来源的 媒体类型(以前称为 MIME 类型)。这为浏览器提供了它需要的所有信息,以立即确定是否可以解码该 source 提供的图片候选项,而无需发出任何外部请求 - 如果媒体类型无法识别,则 <source> 及其所有候选项都将被忽略,并且浏览器将继续前进。

<picture>
 <source type="image/webp" srcset="pic.webp">
 <img src="pic.jpg" alt="...">
</picture>

在这里,任何支持 WebP 编码的浏览器都将识别 <source> 元素的 type 属性中指定的 image/webp 媒体类型,选择该 <source>,并且 - 由于我们仅在 srcset 中提供了一个候选项 - 指示内部 <img> 请求、传输和呈现 pic.webp。任何支持 WebP 的浏览器都将忽略 source,并且在没有任何相反指令的情况下,<img> 将呈现 src 的内容,就像自 1992 年以来所做的那样。当然,您无需在此处指定第二个 <source> 元素,其中 type="image/jpeg" - 您可以假设对 JPEG 的通用支持。

无论用户的浏览上下文如何,所有这些都是通过单个文件传输实现的,并且不会浪费带宽在无法呈现的图片来源上。这也是具有前瞻性的:随着更新、更高效的文件格式将带有自己的媒体类型,我们将能够借助 picture 来利用它们 - 无需 JavaScript,无需服务器端依赖项,并且具有 <img> 的所有速度。

响应式图片的未来

此处讨论的所有标记模式在标准化方面都是一项繁重的工作:更改像 <img> 这样已建立且对 Web 至关重要的内容的功能绝非易事,并且这些更改旨在解决的一系列问题至少是广泛的。如果您发现自己认为这些标记模式有很多改进空间,那么您绝对是对的。从一开始,这些标准就旨在为未来的技术奠定基础。

所有这些解决方案都必须依赖标记,以便包含在来自服务器的初始负载中,并及时到达浏览器以请求图片来源 - 这种限制导致了公认的笨拙的 sizes 属性。

但是,自从将这些功能引入 Web 平台以来,引入了一种延迟图片请求的本机方法。具有 loading="lazy" 属性的 <img> 元素在页面布局已知后才会请求,以便延迟请求用户初始视口之外的图片,直到页面渲染过程的后期,从而可能避免不必要的请求。由于浏览器在发出这些请求时完全了解页面布局,因此 sizes="auto" 属性已被提议作为 HTML 规范的补充,以避免在这些情况下手动编写 sizes 属性的麻烦。

随着我们页面布局样式发生的一些非常令人兴奋的变化,<picture> 元素也即将进行添加。虽然视口信息是高级布局决策的良好基础,但它阻止我们采用完全组件级的开发方法 - 这意味着,一个组件可以放入页面布局的任何部分,其样式可以响应组件本身占用的空间。这种担忧导致了容器查询的创建:一种基于其父容器的大小而不是仅基于视口来设置元素样式的方法。

虽然容器查询语法才刚刚稳定 - 并且在撰写本文时浏览器支持非常有限 - 但启用它的浏览器技术的添加将为 <picture> 元素提供一种做同样事情的方法:一个潜在的 container 属性,允许基于 <picture> 元素的 <img> 占用的空间而不是基于视口大小的 <source> 选择标准。

如果这听起来有点模糊,那么,这是有充分理由的:这些 Web 标准讨论正在进行中,但远未定论 - 您还不能使用它们。

虽然响应式图片标记有望随着时间的推移变得更容易使用,但像任何 Web 技术一样,有许多服务、技术和框架可以帮助减轻手动编写此标记的负担。在下一个模块中,我们将研究如何将我们学到的有关图片格式、压缩和响应式图片的所有知识集成到现代开发工作流程中。