术语“响应式图像”已成为“HTML 中的响应式图像”的代名词,换句话说,就是 <img>
的 srcset
和 sizes
属性以及 <picture>
元素。 但是这些功能是如何映射到 CSS 的呢?
CSS 通常并没有真正参与过去几年响应式图像的旅程。 这是有充分理由的:CSS 已经拥有这些工具。 响应式图像从某种意义上说,只是赶上了 CSS 已经可以做的事情。 让我们来看一下。
srcset
CSS 中的 在 HTML 中,srcset
就像这样(取自 Picturefill 网站)
<img srcset="
examples/images/image-384.jpg 1x,
examples/images/image-768.jpg 2x
" alt="…">
一个图像用于 1x 显示,一个更大的图像用于 2x 显示。 如果我们想做同样的事情,只是作为 CSS 中的 background-image
,我们可以这样做
.img {
background-image: url(examples/images/image-384.jpg);
}
@media
(-webkit-min-device-pixel-ratio: 2),
(min-resolution: 192dpi) {
.img {
background-image: url(examples/images/image-768.jpg);
}
}
不过这里确实存在差异。 据我了解,srcset
的规范方式有点像建议。 属性和值提供了有关可用内容的信息,浏览器决定此时此刻最适合的内容。 或者至少,它可以,如果浏览器选择以这种方式实现它。 使用 @media
查询,声明的内容将被执行。
分辨率媒体查询得到了相当好的支持

还有另一种方法,实际上更接近 srcset
的工作方式,那就是在 CSS 中使用 image-set()
函数
.img {
background-image: url(examples/images/image-384.jpg);
background-image:
-webkit-image-set(
url(examples/images/image-384.jpg) 1x,
url(examples/images/image-768.jpg) 2x,
);
background-image:
image-set(
url(examples/images/image-384.jpg) 1x,
url(examples/images/image-768.jpg) 2x,
);
}
它比分辨率查询的支持度低一些

它更接近 srcset
,不仅因为语法相似,还因为它允许浏览器发挥作用。 根据 (仍在草案阶段)规范
image-set() 函数允许作者忽略大多数这些问题,只需提供图像的多种分辨率,并让 UA 决定在特定情况下最合适哪种分辨率。
在 CSS 中没有完美的 srcset
一对一替代方案,但这种方法非常接近。
sizes
CSS 中的 HTML 中的 sizes
属性与 CSS 直接相关。 实际上,它基本上是说:“这就是我打算在 CSS 中设置图像大小的方式,我只是现在告诉您,因为您可能马上就需要此信息,而不能等到 CSS 先下载。”
示例
<img
sizes="(min-width: 40em) 80vw, 100vw"
srcset=" ... "
alt="…">
假设 CSS 中有类似以下内容
img {
width: 100%;
}
@media (min-width: 40em) {
/* Probably some parent element that limits the img width */
main {
width: 80%;
}
}
但是,sizes
本身什么也不做。 您需要将其与 srcset
配合使用,srcset
提供已知的宽度,以便浏览器做出选择。 让我们假设只有两张图像,例如
<img
sizes="(min-width: 400px) 80vw, 100vw"
srcset="examples/images/small.jpg 375w,
examples/images/big.jpg 1500w"
alt="…">
上面的标记信息提供了浏览器确定最佳图像所需的信息。 浏览器知道 1)它自己的视窗大小和 2)它自己的像素密度。
假设浏览器视窗宽度为 320px 且它是 1x 显示。 它现在还知道将以 100vw 的宽度显示此图像。 因此它必须在提供的两张图像中进行选择。 它会进行一些计算。
375(图像 #1 的大小)/ 320(用于显示图像的像素)= 1.17
1500(图像 #2 的大小)/ 320(用于显示图像的像素)= 4.69
1.17 更接近 1(它是 1x 显示),因此 375w 图像胜出。 它会尽量不低于 1,因此 1.3 会胜过 0.99,据我了解是这样。
现在假设它是 2x 显示。 这会将显示图像所需的像素数量增加一倍,因此计算结果为
375 / 640 = 0.59
1500 / 640 = 2.34
2.34 胜出,因此它将显示 1500w 图像。 如果是 1x 显示,视窗宽度为 1200px 会怎样?
375 /(1200 的 80%)= 0.39
1500 /(1200 的 80%)= 1.56
这里 1500w 图像胜出。
这在 CSS 中写出来有点奇怪和棘手。 如果我们只考虑 1x 显示,我们最终会得到以下逻辑…
- 如果视窗小于 375px,则使用 375w 图像。
- 如果视窗大于 375px 但小于 400px,则使用 1500w 图像(因为否则我们将放大)。
- 在 400px 处,图像宽度变为 80vw,因此可以在很短的时间内(在 400px 到 468px 之间)安全地使用 375w 图像()。
- 超过 468px,则使用 1500w 图像。
我们可以这样写
img {
background-image: url(small.jpg);
}
/* Only override this if one of the conditions for the 1500w image is met */
@media
(min-width: 375px) and (max-width: 400px),
(min-width: 468px) {
main {
background-image: url(large.jpg);
}
}
在本例中,即使在 300px 这样的非常窄的宽度下,2x 显示仍然需要 600px 才能使质量达到至少 1.0,因此我们也要将此逻辑添加到逻辑中
.img {
background-image: url(small.jpg);
}
/* Only override this if one of the conditions for the 1500w image is met */
@media
(min-width: 375px) and (max-width: 400px),
(min-width: 468px),
(-webkit-min-device-pixel-ratio: 2),
(min-resolution: 192dpi) {
.img {
background-image: url(large.jpg);
}
}
断点(大小)越多,提供的图像越多,这种复杂性就会呈几何级数增长。 而且它仍然不能完美地匹配响应式图像(在 HTML 中)的功能,因为它不允许浏览器自由裁量(例如,浏览器可能考虑其他因素 [即带宽] 来选择图像)。
picture
CSS 中的 一个示例
<picture>
<source srcset="extralarge.jpg" media="(min-width: 1000px)">
<source srcset="large.jpg" media="(min-width: 800px)">
<img srcset="medium.jpg" alt="…">
</picture>
这种方法可以相当直观地转换为媒体查询。 确切的媒体查询就在那里可以复制
.img {
background-image: url(medium.jpg);
}
@media (min-width: 800px) {
.img {
background-image: url(large.jpg);
}
}
@media (min-width: 1000px) {
.img {
background-image: url(extralarge.jpg);
}
}
毫不奇怪,这会变得更加复杂,因为 srcset
也可以在 picture
元素中执行其操作
<picture>
<source srcset="large.jpg, extralarge.jpg 2x" media="(min-width: 800px)">
<img srcset="small.jpg, medium.jpg 2x" alt="…">
</picture>
这转换为
.img {
background-image: url(small.jpg);
}
@media
(-webkit-min-device-pixel-ratio: 2),
(min-resolution: 192dpi) {
.img {
background-image: url(medium.jpg);
}
}
@media
(min-width: 800px) {
.img {
background-image: url(large.jpg);
}
}
@media
(-webkit-min-device-pixel-ratio: 2) and (min-width: 800px),
(min-resolution: 192dpi) and (min-width: 800px) {
.img {
background-image: url(extralarge.jpg);
}
}
同样,这只是在添加更多图像和条件后等待变得更加复杂。 使用 image-set()
会稍微好一些
.img {
background-image: url(small.jpg);
background-image:
-webkit-image-set(
"small.jpg" 1x,
"medium.jpg" 2x,
);
background-image:
image-set(
"small.jpg" 1x,
"medium.jpg" 2x,
);
}
@media
(min-width: 800px) {
.img {
background-image: url(large.jpg);
background-image:
-webkit-image-set(
"large.jpg" 1x,
"extralarge.jpg" 2x,
);
background-image:
image-set(
"large.jpg" 1x,
"extralarge.jpg" 2x,
);
}
}
混合器可以帮助吗?
可能可以? 我很想看到一个 Sass @mixin
,它可以接收所有这些参数,考虑图像大小(它自己通过访问磁盘来确定),并输出一些高质量的响应式图像 CSS 代码。 也许甚至可以将分辨率查询和 image-set()
语法结合起来?
我们真的在这样做吗?
我很想知道有多少人正在积极使用 CSS 处理响应式图像。 也许甚至只是在大型断点处交换更大的图像这样的中间方案? 我想知道,因为 picture/srcset 通常是自动化的,所以它的采用率实际上是否比 CSS 中的响应式图像更高?
srcsets 一直让我很困扰。 谢谢,Chris!
image-set
当然似乎是最佳选择,但在浏览器支持提高之前,我不得不使用@supports
和 JavaScript 检测的组合来实现它。 最坏的情况是,我会先给浏览器提供移动图像,然后在 JavaScript 中尽快用依赖于浏览器分辨率的备用图像替换它。感谢这篇文章。 我发现您在文章最后提到的方法是最简单的(在较大的断点处交换背景图像)。 通常情况下我会使用一张移动图像和一张全尺寸图像来保持简单。
是的,这就是我所做的。 我使用一个简单的混合器来根据断点映射中的条目加载适当的背景图像。 没什么花哨的,但它确实完成了这项工作,最重要的是,我知道在特定情况下会加载哪一个。
很棒的文章,Chris,我之前不知道您介绍的很多信息。 我目前在我的个人网站上(我不会链接)只使用 srcset 属性。 我对如何使用 sizes 属性有点困惑...所以如果您能向我解释一下语法,我会非常感激。 无论如何,很棒的文章。
使用
Width: auto;
Height: auto;
Max-height: auto;
Max-width: auto;
并根据不同的屏幕尺寸设置其父级元素的固定尺寸。
比如
Width: 160px;
Height: 160px;
这样做是为了只为您的设备加载最佳图像,这样您就不必在每个设备上都下载非常高分辨率的图片。
感谢您发表的文章,Chris。我来自 Yaphi。我在不同的断点处替换背景图像,通常不超过 2 或 3 张图像。桌面、移动端,有时会多加一张来处理页眉图像和 CTA 的断开。保持简单。
很棒的文章!
我们公司一直在使用它来减少下载的图像大小,特别是对于全屏图像,这可以将网站的带宽从几 MB 减少到不到 1 MB。我们主要使用 html 版本,因为我们所有的图像都是运行时上传的。
我发现的问题是,使用 bootstrap 列时,很难在不同的断点上进行正确集成。
我强烈建议您尝试使用 Cloudinary 或 ImgIX 来自动设置此类内容。当然,如果需要,您可以完全自己设置,但这两家公司都价格实惠,而且能够提供一些非常棒的功能。顺便说一下,我与他们没有任何关系,也没有从中获利;我只是在各种项目中使用过这两家公司的服务,而且它确实很棒,无需再处理图像问题了。
无论如何,感谢您一如既往地撰写文章,Chris!
很棒的文章。我在几个网站上使用过 srcset 来修复一些浏览器处理画廊时糟糕的方式。
值得注意
https://github.com/square/apropos
apropos 看起来很有前景,感谢您的分享!
我在最近的项目中试用了 https://github.com/paper-leaf/waldo,虽然这更像 PHP 而不是 CSS,并且很大程度上基于 WP / ACF,但在我看来,同样值得注意。
对于背景图像,使用混合来在特定断点处加载正确的图像要容易得多,但您在文章中的封面对内联图像很有趣。感谢您!
好文章,但是,虽然提出的解决方案可以解决一些问题,但在 IMG 上使用背景图像违反了 html 语义,并且会降低可访问性。
img 应该具有补充内容的含义,而背景图像则用于装饰目的。
我绝不暗示您应该将内容图像移到 CSS 背景图像中。