• 聊聊Chrome即将新增的 sizes=auto 属性
  • 发布于 2个月前
  • 279 热度
    0 评论
今天和大家来一起聊聊 Chrome 即将新增的 sizes=auto 属性。

在网页开发中,我们通常会遇到需要对网页上的元素,尤其是图片进行布局和尺寸调整的情况。在 HTML 中,我们可以利用一些内置的属性来帮助我们完成这项工作,而 sizes 就是其中之一。sizes 属性定义了用于描述其中 <img>、<source> 元素 display size,以及 <link> 元素 viewport 的 source 大小。换句话说,它是一种设定网页元素尺寸的机制。

编写、阅读和维护响应式图片的 sizes 属性是最繁琐的部分;对于懒加载的图片,auto-sizes 是对平台的一个很好的补充。据统计,目前已有十分之一的 sizes 属性正在使用 auto,但是是通过通过 lazysizes.js(一个用于支持图片懒加载的库)。

sizes 支持了 "auto" 值,那么情况就会变得稍微有些复杂。所谓的 "auto" 模式,实际上是一种让浏览器自动设定图片尺寸的模式。在这种模式下,浏览器会试图选择最合适的尺寸来显示图片,以获得最佳的用户体验。它的应用场景主要是在网站设计和开发过程中,需要灵活设定元素尺寸,尤其是图片的尺寸,以便于在不同设备和浏览器环境下,都能呈现出良好的布局和视觉效果。

原生的 auto-sizes 最近在 Chrome Canary 版本的 Experimental Web Platform Features 标志下发布。发布后我就去试用了一下,但是发现了一个奇怪的问题:除了需要设置 loading=lazy 外,sizes=auto 基本上也需要 <img> 元素有宽度和高度属性。这里有一段来自 HTML 规范第 4.8.2.2 节,关于 sizes 属性的注释:

注:[…] 强烈建议使用 width 和 height 属性或 CSS 来指定尺寸。如果未指定尺寸,由于 sizes="auto" 暗含 contain-intrinsic-size: 300px 150px,图片可能会以 300x150 的尺寸呈现。

啥!?300×150?
事实的确如此!这里有一个使用了 sizes=auto 的 <img> 元素:
<img
 loading="lazy"
 sizes="auto"
 srcset="
  https://o.img.rodeo/w_1200,h_1200,b_tomato/t_WxH/_.png   1200w,
  https://o.img.rodeo/w_900,h_900,b_goldenrod/t_WxH/_.png   900w,
  https://o.img.rodeo/w_600,h_600,b_forestgreen/t_WxH/_.png 600w,
  https://o.img.rodeo/w_300,h_300,b_dodgerblue/t_WxH/_.png  300w"
 src="https://o.img.rodeo/w_300,h_300,b_forestgreen/t_WxH/_.png"
 alt="An example image that reports its natural dimensions"
>
以下是在启用实验性标志的 Chrome Canary 中,该元素现在的样子:

在我们探讨到底发生了什么,为什么之前,让我们先解决实际问题:我们该如何解决它?答案是你需要设置的高度和宽度是 <img> 标签可以调用的最大资源的像素尺寸。如果你使用了 srcset 属性,那就意味着你需要设置的是 srcset 里最大的图片资源的尺寸(如果你在使用 Client Hints,那么应参考未经服务器端缩放处理的原图尺寸。):
<img
 loading="lazy"
 sizes="auto"
 width="1200"
 height="1200"
 srcset="
  https://o.img.rodeo/w_1200,h_1200,b_tomato/t_WxH/_.png   1200w,
  https://o.img.rodeo/w_900,h_900,b_goldenrod/t_WxH/_.png   900w,
  https://o.img.rodeo/w_600,h_600,b_forestgreen/t_WxH/_.png 600w,
  https://o.img.rodeo/w_300,h_300,b_dodgerblue/t_WxH/_.png  300w"
 src="https://o.img.rodeo/w_300,h_300,b_dodgerblue/t_WxH/_.png"
 alt="An example image that reports its natural dimensions"
>
你的 CSS 中的其他设置可能会多多少少影响到添加在 HTML 中的高度和宽度,令 <img> 的显示比例不合适,比如,过高的固定高度可能与它本应灵活的宽度搭配不协调。只需一行CSS代码,就能轻松解决这个问题:
img {
 height: auto; 
}
然后全部正常的代码应该是:
<style>
img {
 width: 100%;
 height: auto;
}
</style>

<img
 loading="lazy"
 sizes="auto"
 width="1200"
 height="1200"
 srcset="
  https://o.img.rodeo/w_1200,h_1200,b_tomato/t_WxH/_.png   1200w,
  https://o.img.rodeo/w_900,h_900,b_goldenrod/t_WxH/_.png   900w,
  https://o.img.rodeo/w_600,h_600,b_forestgreen/t_WxH/_.png 600w,
  https://o.img.rodeo/w_300,h_300,b_dodgerblue/t_WxH/_.png  300w"
 src="https://o.img.rodeo/w_300,h_300,b_dodgerblue/t_WxH/_.png"
 alt="An example image that reports its natural dimensions"
>
下面我们再来看看效果:

也许你已经在你的响应式图像 HTML 中添加了高度和宽度,并在你的 CSS 中添加了 height: auto 来防止布局发生变化。那挺好,就不需要做任何改造!
知道了咋解决,下面我们来看看到底发生了啥,为什么会这样?

首先,网页上的图片默认会以其自然(也被称为固有)尺寸进行渲染。如果你在标签中添加了一个 300×300 的资源,并且没有进行任何其他操作,那么它的布局尺寸就是 300×300。

响应式图像自然尺寸的设定可能会让人意想不到的复杂,但实际上:
1.有许多方式可以让已加载资源的自然尺寸影响其对应的 <img> 的布局尺寸。
2.每当 <img> 标签的布局尺寸发生变化,sizes=auto 的值就会进行更新,这可能会触发新的资源加载。
3.一旦加载新的资源,这个资源就会有新的自然尺寸,这可能会影响 <img> 的布局尺寸,导致 sizes=auto 的值更新,然后可能会触发又一次的资源加载;新加载的资源会有新的自然尺寸,这样以此类推...等等。

关于 sizes=auto 的规范讨论其实主要就是:那些能通过这种方式让 srcset 中的每一个资源依次被加载的极端情况。

Simon Pieters,撰写了 auto-sizes 规范的人,曾经巧妙并勇敢地尝试定义可能触发循环依赖的所有情况,同时也采用了一些无法让作者察觉或是感到奇怪的解决方式来避开这些问题。但是,当真正落实到实现的时候,这些权宜之计并没有站住脚。最后,大家都认为唯一的解决方法是做出一个明确的切割:规定加载资源的自然尺寸绝对不能影响其所对应的 <img> 的布局,这一点在涉及到 sizes=auto 时尤为重要。

幸运的是,我们有一个相对较新的特性,它就是“尺寸包含”(size containment),专门用来解决这个问题。

现在,这是 Chrome Canary 的 UA stylesheet 中的内容:
img:is([sizes="auto" i], [sizes^="auto," i]) {
    contain: size !important;
    contain-intrinsic-size: 300px 150px;
}
这句意思是:我们在处理带有 sizes=auto 的 <img> 标签吗?如果是,那么它的自然尺寸就是 300×150,绝无例外。对于为什么选择 300×150,是因为这就是 <video> 和 <canvas> 标签所采取的方式;所有这些元素都会使用这种相对小一些但又不为零的默认尺寸,目的在于鼓励你优化你的布局。

实际上,设定 width 和 height 并不是达成效果的唯一途径!我个人认为,在 HTML 中定义你的内容比例,然后在 CSS 中赋予其灵活的尺寸,能比其他所有的解决方案更完美地实现关注点的分离。但是,至关重要的一个观点就是:当你采用 sizes=auto 时,你的图片会默认获得一个 300×150 的固定尺寸和 2:1 的固定比例,如何挑战或者改变这些默认设定,就取决于你大家的需求和想象力了。

大家觉得这个属性怎么样?欢迎来评论区留言。
用户评论