了解一下 picture 元素。
<picture>
元素本身不会渲染任何内容,而是作为内部<img>
元素的决策引擎,告诉它应该渲染什么。<picture>
遵循了<audio>
和<video>
元素已经设置的先例:一个包含单独<source>
元素的包装器元素。
<picture>
<source …>
<source …>
<img …>
</picture …>
那个内部的<img>
元素也为我们提供了一个可靠的回退模式,用于不支持响应式图片的旧浏览器:如果用户的浏览器不识别<picture>
元素,它将被忽略。然后,<source>
元素也将被丢弃,因为浏览器要么根本不识别它们,然而,内部的<img>
元素将被任何浏览器识别,而其src
属性指定的源将如预期地渲染。
根据图像在页面中的大小进行内容或纵横比的更改,通常被称为“art directed” 响应式图像 。srcset
和sizes
旨在无缝地工作,根据用户浏览器的指示无缝地交换源。然而,有时我们希望在断点处更改源以更好地突出内容,就像调整页面布局一样。例如:在大视口上,带有小中央焦点的全宽头图像可能效果很好:
但是,当缩小以适应小视口时,图像的中央焦点可能会丢失:
这些图像源的主题相同,但为了更好地视觉聚焦于该主题,我们将希望图像源的比例在断点处发生变化。例如,对图像中心进行更紧密的缩放,并剪切掉一些边缘的细节:
这种“剪切”可以通过CSS实现,但会使用户请求组成该图像的所有数据,即使他们可能永远不会看到它。
每个 source
元素都有定义选择该源的条件的source: media
,它接受媒体查询,以及类型,它接受媒体类型(以前称为“MIME类型”)。在源顺序中与用户当前浏览上下文匹配的第一个<source>
将被选择,并且该源的srcset
属性的内容将用于确定该上下文的正确候选项。在此示例中,第一个具有与用户视口大小匹配的 media
属性的源将被选中:
<picture>
<source media="(min-width: 1200px)" srcset="wide-crop.jpg">
<img src="close-crop.jpg" alt="…">
</picture>
地址:
https://codepen.io/web-dot-dev/pen/poZNxyN
我们应该始终在顺序中指定内部img
的最后一个——如果没有一个<source>
元素与其media
或 type
条件匹配,该图像将充当“默认”源。如果你使用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来优化"art directed"图像源。
<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>
只支持单个宽度和高度属性,但省略这些属性会导致用户体验明显变差。为了解决这个问题,HTML规范的一个相对较新但得到很好支持的补充允许在<source>
元素上使用高度和宽度属性。这些属性的作用与在<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>
需要注意的是,"art direction"不仅仅可以用于基于视口大小的决策,而且应该使用,因为在大多数情况下,这些情况可以通过srcset / sizes
更有效地处理。例如,选择更适合用户偏好指定的颜色方案的图像源:
<picture>
<source media="(prefers-color-scheme: dark)" srcset="hero-dark.jpg">
<img srcset="hero-light.jpg">
</picture>
示例地址:https://codepen.io/web-dot-dev/pen/MWBbPJm
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的浏览器将忽略该源,如果没有任何相反的指示,<img>
将像自1992年以来一样渲染src
的内容。当然,在这里不需要指定第二个type="image/jpeg"
的<source>
元素——可以假设所有浏览器都支持JPEG。
无论用户的浏览上下文如何,这一切都通过单个文件传输实现,而不会浪费带宽在不能呈现的图像源上。这也是前瞻性的:随着更新更高效的文件格式将带有自己的媒体类型,我们将能够借助picture
利用它们——无需JavaScript、无服务器依赖,并且具有<img>
的所有速度。
在这里讨论的所有标记模式在标准化方面都是一个巨大的挑战:改变像<img>
这样已经成为Web核心的东西的功能不是一件小事,而这些变化旨在解决的问题集也是相当广泛的。如果你发现自己认为这些标记模式有很大的改进空间,那么你是完全正确的。从一开始,这些标准就旨在为未来的技术提供基准。
所有这些解决方案都必须依赖标记,以便包含在服务器的初始负载中,并及时到达浏览器请求图像源,这一限制导致了明显笨重的sizes
属性。
然而,自从这些特性被引入到Web平台以来,就引入了一种延迟图像请求的本地方法。具有loading="lazy"
属性的<img>
元素直到页面布局已知才被请求,以便推迟对用户初始视口之外的图像的请求,直到在渲染页面的过程中稍后进行,从而避免不必要的请求。因为浏览器在这些请求发出时完全了解页面布局,因此已经提出了一个sizes="auto"
属性作为HTML规范的附加内容,在这些情况下避免手动编写sizes
属性的繁琐工作。
此外,<picture>
元素也即将有一些新的改进,以匹配页面布局样式方面的一些极其令人兴奋的变化。虽然基于视口信息的高级布局决策是可靠的,但它阻止了我们采用完全基于组件层级的开发方法,这意味着可以将组件放置在页面布局的任何部分,并响应组件本身所占用的空间的样式。这种情况促使出现了容器查询:一种基于父容器大小而非视口大小来为元素设置样式的方法。
虽然容器查询语法才刚刚稳定下来,而且在撰写本文时,浏览器支持非常有限,但启用它的浏览器技术的增加将为 <picture>
元素提供同样的方法:一种潜在的容器属性,它允许基于 <picture>
元素的 <img>
所占用的空间而非基于视口大小来选择 <source>
。
如果听起来有点模糊,那么这是有很好的原因的:这些 Web 标准的讨论仍在进行中,远未定案,目前还不能使用它们。
虽然响应式图片标记承诺随着时间的推移只会变得更易于使用,但像任何 Web 技术一样,有许多服务、技术和框架可用于帮助减轻手写此标记的负担。在下一个模块中,我们将学习如何将我们所学的有关图像格式、压缩和响应式图片的所有内容集成到现代开发工作流程中。
代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug。