有时 `sizes` 非常重要。

Avatar of Chris Coyier
Chris Coyier

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 200 美元的免费额度!

来自电子邮件的释义问题

我刚刚阅读了您的文章 响应式图片:如果您只是更改分辨率,请使用 srcset。 在“响应式网站”时代,srcset 在某些情况下无济于事。 例如,我有一个热门产品滑块。 在移动设备上,每个幻灯片一张图片,图片宽度为 320px。 在桌面设备上,每个幻灯片六张图片,每张图片宽度为 160px。 因此,桌面图片在桌面设备上更小,而不是更大。

我该如何使用 srcset 处理这种情况?

我试图谨慎地选择这篇文章的标题:“如果您只是更改分辨率,请使用 srcset”。 在这种情况下,我们不仅在某些分辨率下更改图片的大小,而且在特定的断点处也会更改,这意味着我们还需要使用 sizes 属性才能充分利用响应式图片。 sizes 属性的全部工作就是根据我们的 CSS 告诉浏览器图片将显示的大小。

演示!调整宽度以查看它在“桌面”和“移动”视图之间交替显示。

查看 CodePen 上 Chris Coyier (@chriscoyier) 编写的笔 响应式图片滑块

如电子邮件中所述,“桌面”版本实际上呈现的图片大小(160px)小于“移动”版本(320px)。

我们还要考虑 2 倍显示屏。 为此,我们准备三个版本的每个图片

  • 160px(适用于 1 倍桌面显示屏)
  • 320px(适用于 2 倍桌面显示屏或 1 倍移动显示屏)
  • 640px(适用于 2 倍移动显示屏)

使用 srcset,看起来像这样

<img srcset="
  food-big.jpg 640w,
  foot-medium.jpg 320w,
  food-small.jpg 160w"
/>

请注意,我们尚未使用 sizes 属性。 浏览器会假设您可能将此图片渲染为 100vw 宽。 这很不幸,因为浏览器可能会下载比需要的更大的图片,而这正是我们使用响应式图片想要解决的问题。

在我的 2 倍桌面显示屏上,我可以看出它正在下载 640px 版本,但它实际上只需要 320px 版本。

正是我们的 CSS 控制了这些图片的渲染大小。 事实上,在此演示中,我们有一个媒体查询,它说:“浏览器,仅在桌面显示屏上将图片渲染为 160px 宽”。

@media (min-width: 600px) {
  .slider img {
    width: 160px;
  }
}

您可能会认为浏览器会知道这一点,它确实知道——它只需要先下载并解析 CSS。 浏览器希望比这更快地做出下载决策。 因此,让我们使用 sizes 属性告诉它。

<img srcset="
  food-big.jpg 640w,
  foot-medium.jpg 320w,
  food-small.jpg 160w"
 
  sizes="(min-width: 600px) 160px, 320px"
/>

也就是说,“好的,我们将在桌面上将此图片渲染为 160px 宽。 否则,让我们使用 320px 宽”。 使用它,我们可以看到浏览器做出了正确的选择

浏览器现在只选择下载 320px 版本,这在 2 倍显示屏上是正确的,因为图片在 160px 处渲染。

为了确保,这是一个窄视口(例如“移动”设备)

我们又回到了下载 640px 版本,这是正确的,因为在这个视口大小下,sizes 属性告诉浏览器我们打算在 320px 处渲染,并且我们使用的是 2 倍显示屏。

这里还有更多细微差别

我向 Eric “Sizes 大师” Portis 展示了这个演示,他证实了所有这些,但还有一些特定于浏览器的补充。 我将总结一下(但请注意这篇博文的日期,因为这些东西往往会发生变化)

  • Firefox 的行为完全如上所述。
  • Chrome 也是如此,除了如果它在缓存中具有该版本,它将始终使用较大版本。 因此,如果它在缓存中具有 640px 版本,它知道它实际上只需要 320px 版本,但由于它在缓存中没有 320px 版本,因此它将改为使用 640px 版本。
  • Safari 也是如此,除了在做出选择后,它永远不会更改(例如,如果您调整浏览器窗口大小)。
  • 关于 srcset 的一个有趣之处在于,规范允许它以任何方式做出选择,也许使用网络条件来决定。 大多数浏览器还没有这样做,除了 Chrome,如果 HTML 文档上有一个 Save-Data 标头,它会下载最小的资源。
  • Joe McGill 还指出:“对于不支持 srcsetw 描述符的旧版 iOS 设备,将使用列表中的第一个源项,因此如果您支持旧版 iOS 设备,则可能需要以首选的默认大小开头”。 换句话说,这些旧版 iOS 设备可能支持响应式图片的早期语法,但仅支持 x 描述符(如 big-image.jpg 2x),因此也许可以将一个好的默认图片指定为 srcset 中的第一个。

查看 Eric 的分支可能会有所帮助,以便更轻松地查看下载的内容

查看 CodePen 上 Eric Portis (@eeeps) 编写的笔 响应式图片滑块

随机笔记

  • 我从 Unsplash 获取了这些食物照片。
  • 我将它们上传到 Cloudinary,以便我可以使用 URL 参数调整它们的大小,而不必自己处理。
  • 它们都是正方形,不是因为我这样下载的,而是因为我在 CSS 中调整了它们的大小,并使用 object-fit 防止了挤压。 这意味着正在下载不需要的图片数据,因此我应该自己裁剪它们或使用 Cloudinary URL 参数 来做到这一点。
  • 我在演示中使用了 Pug HTML 预处理器,只是为了减少代码重复。

这更容易查看

img(srcset=`
  ${img_base}w_640${img_1} 640w,
  ${img_base}w_320${img_1} 320w,
  ${img_base}w_160${img_1} 160w,
` sizes=sizes)

…而不是输出

<img srcset="
    https://res.cloudinary.com/css-tricks/image/upload/c_scale,f_auto,q_auto,w_640/v1531407936/robin-stickel-82145-unsplash_qnexwz.jpg 640w,
    https://res.cloudinary.com/css-tricks/image/upload/c_scale,f_auto,q_auto,w_320/v1531407936/robin-stickel-82145-unsplash_qnexwz.jpg 320w,
    https://res.cloudinary.com/css-tricks/image/upload/c_scale,f_auto,q_auto,w_160/v1531407936/robin-stickel-82145-unsplash_qnexwz.jpg 160w,
  " sizes="(min-width: 600px) 160px, 320px"/><img srcset="
    https://res.cloudinary.com/css-tricks/image/upload/c_scale,f_auto,q_auto,w_640/v1531407936/cel-lisboa-60315-unsplash_qsji9j.jpg 640w,
    https://res.cloudinary.com/css-tricks/image/upload/c_scale,f_auto,q_auto,w_320/v1531407936/cel-lisboa-60315-unsplash_qsji9j.jpg 320w,
    https://res.cloudinary.com/css-tricks/image/upload/c_scale,f_auto,q_auto,w_160/v1531407936/cel-lisboa-60315-unsplash_qsji9j.jpg.jpg 160w,
  " sizes="(min-width: 600px) 160px, 320px"/><img srcset="
    https://res.cloudinary.com/css-tricks/image/upload/c_scale,f_auto,q_auto,w_640/v1531407936/charles-deluvio-466056-unsplash_ocd3dh.jpg 640w,
    https://res.cloudinary.com/css-tricks/image/upload/c_scale,f_auto,q_auto,w_320/v1531407936/charles-deluvio-466056-unsplash_ocd3dh.jpg 320w,
    https://res.cloudinary.com/css-tricks/image/upload/c_scale,f_auto,q_auto,w_160/v1531407936/charles-deluvio-466056-unsplash_ocd3dh.jpg.jpg 160w,
  " sizes="(min-width: 600px) 160px, 320px"/>