在 HTML 中使用响应式图像
在本文中,我们将学习响应式图像的概念——在具有各种屏幕尺寸、分辨率和其他此类功能的设备上都能很好地工作的图像——并了解 HTML 提供了哪些工具来帮助实现它们。这有助于提高不同设备上的性能。
为什么需要响应式图像?
让我们检查一个典型的场景。一个典型的网站可能包含一个页眉图像和页眉下方的一些内容图像。页眉图像可能会横跨页眉的整个宽度,而内容图像将适合内容列中的某个位置。这是一个例子
这在宽屏设备(例如笔记本电脑或台式机)上运行良好(您可以在线查看示例,并在 GitHub 上找到源代码)。在本课程中我们不会过多讨论 CSS,除了说
- 主体内容已设置为最大宽度 1200 像素——在超过该宽度的视口中,主体保持 1200 像素并自行在可用空间中居中。在小于该宽度的视口中,主体将保持视口宽度的 100%。
- 页眉图像已设置,因此其中心始终保持在页眉的中心,无论页眉设置为多宽。如果网站在较窄的屏幕上查看,图像中心的重要细节(人物)仍然可见,而多余的部分则丢失在两侧。它的高度为 200 像素。
- 内容图像已设置,如果主体元素变得小于图像,图像将开始缩小,以便它们始终保持在主体内部,而不是溢出。
然而,当您开始在窄屏设备上查看网站时,就会出现问题。下面的页眉看起来还不错,但对于移动设备来说,它开始占据了大量的屏幕高度。而且在这个尺寸下,很难看清第一个内容图像中的两个人的面孔。
一种改进方法是,当在窄屏上查看网站时,显示图像的裁剪版本,该版本显示图像的重要细节。对于平板电脑等中等宽度屏幕设备,可以显示第二个裁剪图像。这种针对各种布局提供不同裁剪图像的普遍问题通常被称为艺术指导问题。
此外,如果是在移动屏幕上查看,则无需在页面中嵌入如此大的图像。这样做会浪费带宽;特别是,移动用户不希望通过下载为桌面用户准备的大图像来浪费带宽,因为一个小图像就足以满足他们的设备需求。相反,当小型光栅图像显示大于其原始尺寸时,它会开始显得颗粒状(光栅图像是固定数量的像素宽度和固定数量的像素高度)。理想情况下,应该向用户的网络浏览器提供多种分辨率。然后浏览器可以根据用户设备的屏幕尺寸确定要加载的最佳分辨率。这被称为分辨率切换问题。
更复杂的是,有些设备具有高分辨率屏幕,需要比您预期更大的图像才能很好地显示。
您可能认为矢量图像可以解决这些问题,它们在一定程度上确实可以——它们文件大小小,缩放效果好,您应该尽可能使用它们。但是,它们不适用于所有图像类型。矢量图像非常适合简单的图形、图案、界面元素等,但要创建具有您在照片中看到的那种细节的矢量图像会变得非常复杂。JPEG 等栅格图像格式更适合我们上面示例中看到的图像类型。
这种问题在 90 年代初中期互联网刚出现时并不存在——那时唯一存在的浏览网络的设备是台式机和笔记本电脑,所以浏览器工程师和规范编写者甚至没有考虑实现解决方案。响应式图像技术最近才被实施,通过让您向浏览器提供多个图像文件来解决上述问题,这些文件要么都显示相同的内容但包含不同数量的像素(分辨率切换),要么是适合不同空间分配的不同图像(艺术指导)。
如何创建响应式图像?
在本节中,我们将介绍上面说明的两个问题,并展示如何使用 HTML 的响应式图像功能来解决它们。您应该注意,本节我们将重点关注<img>
元素,如上面示例的内容区域所示——网站标题中的图像仅用于装饰,因此使用 CSS 背景图像实现。CSS 可以说比 HTML 拥有更好的响应式设计工具,我们将在未来的 CSS 模块中讨论这些。
分辨率切换:不同尺寸
那么,我们希望通过分辨率切换解决什么问题?我们希望显示相同的图像内容,只是根据设备的大小或小——这就是我们示例中第二个内容图像的情况。传统的标准<img>
元素只允许您将浏览器指向单个源文件
<img src="elva-fairy-800w.jpg" alt="Elva dressed as a fairy" />
但是,我们可以使用两个属性——srcset
和sizes
——来提供几个额外的源图像以及帮助浏览器选择正确图像的提示。您可以在 GitHub 上的responsive.html示例中看到一个例子(另请参见源代码)
<img
srcset="elva-fairy-480w.jpg 480w, elva-fairy-800w.jpg 800w"
sizes="(width <= 600px) 480px,
800px"
src="elva-fairy-800w.jpg"
alt="Elva dressed as a fairy" />
srcset
和 sizes
属性看起来很复杂,但如果像上面那样格式化它们,将属性值的不同部分放在不同的行上,它们就不难理解了。每个值都包含一个逗号分隔的列表,这些列表的每个部分都由三个子部分组成。现在让我们来逐一介绍每个内容
srcset
定义了我们允许浏览器选择的图像集,以及每个图像的大小。每组图像信息之间用逗号分隔。对于每一组,我们写
- 一个图像文件名 (
elva-fairy-480w.jpg
) - 一个空格
- 图像的固有像素宽度(
480w
)——请注意,这里使用的是w
单位,而不是您可能预期的px
。图像的固有大小是其真实大小,可以通过检查计算机上的图像文件来找到(例如,在 Mac 上,您可以在 Finder 中选择图像并按 Cmd + I 以调出信息屏幕)。
sizes
定义了一组媒体条件(例如,屏幕宽度),并指明了当某些媒体条件为真时,最适合选择的图像大小——这些就是我们之前谈到的提示。在这种情况下,在每个逗号之前我们写
- 一个媒体条件(
(width <= 600px)
)——您将在CSS 主题中了解更多关于这些条件的信息,但现在我们只说媒体条件描述了屏幕可能处于的一种状态。在这种情况下,我们说的是“当视口宽度为 600 像素或更小时”。 - 一个空格
- 当媒体条件为真时,图像将填充的槽位宽度 (
480px
)
注意:在 sizes
中,您可以使用任何长度值。例如,您可以提供一个相对于视口的宽度(例如,50vw
),而不是提供一个绝对宽度(例如,480px
)。但是,您不能使用百分比作为槽位宽度。您可能已经注意到,最后一个槽位宽度没有媒体条件(当所有媒体条件都不为真时,会选择此默认值)。浏览器会忽略第一个匹配条件之后的所有内容,因此请注意媒体条件的顺序。
因此,有了这些属性,浏览器将
- 查看屏幕尺寸、像素密度、缩放级别、屏幕方向和网络速度。
- 找出
sizes
列表中第一个为真的媒体条件。 - 查看该媒体查询给定的槽位大小。
- 加载
srcset
列表中引用的大小与槽位大小相同的图像。如果显示大小没有精确匹配,浏览器将选择第一个大于所选槽位大小的图像并将其缩小以适应。
就是这样!此时,如果支持的浏览器以 480px 的视口宽度加载页面,则 (width <= 600px)
媒体条件将为真,因此浏览器会选择 480px
槽位。elva-fairy-480w.jpg
将被加载,因为其固有宽度 (480w
) 最接近槽位大小。800px 的图片在磁盘上是 128KB,而 480px 的版本只有 63KB——节省了 65KB。现在,想象一下如果这是一个有很多图片的页面。使用这种技术可以为移动用户节省大量带宽。
注意:在使用桌面浏览器测试时,如果您将窗口设置为最窄宽度时浏览器无法加载较窄的图像,请查看视口是什么(您可以通过进入浏览器的 JavaScript 控制台并输入 document.querySelector('html').clientWidth
来估算)。不同的浏览器有它们允许您减小窗口宽度的最小尺寸,并且它们可能比您想象的要宽。在使用移动浏览器测试时,您可以使用 Firefox 的 about:debugging
页面等工具,使用桌面开发者工具检查加载在移动设备上的页面。
要查看加载了哪些图像,您可以使用 Firefox DevTools 的网络监视器选项卡或 Chrome DevTools 的网络面板。对于 Chrome,您可能还需要禁用缓存以防止它选择已经下载的图像。
不支持这些功能的旧版浏览器将直接忽略它们。相反,这些浏览器将照常加载 src
属性中引用的图像。
注意:在上面链接的示例的<head>
中,您会找到<meta name="viewport" content="width=device-width">
这一行:这会强制移动浏览器采用其真实的视口宽度来加载网页(一些移动浏览器会谎报其视口宽度,而是以更大的视口宽度加载页面,然后缩小加载的页面,这对于响应式图片或设计来说作用不大)。
分辨率切换:相同大小,不同分辨率
假设您有一张图像,它将在具有不同屏幕分辨率的显示器上以相同的真实世界尺寸渲染。通过提供更高分辨率的图像版本,您可以在高分辨率显示器上提供更好的用户体验。
为此,您可以通过使用带有 x 描述符且不带 sizes
的 srcset
来让浏览器选择合适分辨率的图像——语法更简单!您可以在 srcset-resolutions.html 中找到一个示例(另请参见源代码)
<img
srcset="elva-fairy-320w.jpg, elva-fairy-480w.jpg 1.5x, elva-fairy-640w.jpg 2x"
src="elva-fairy-640w.jpg"
alt="Elva dressed as a fairy" />
请注意,即使图像始终以相同尺寸显示,在高分辨率显示器上您也能看到更多细节。
在此示例中,以下 CSS 应用于图像,使其在屏幕上(也称为 CSS 像素)的宽度为 320 像素
img {
width: 320px;
}
在这种情况下,不需要 sizes
——浏览器会根据正在显示的显示器的分辨率来决定,并提供 srcset
中引用的最合适的图像。因此,如果访问页面的设备是标准/低分辨率显示器,每个 CSS 像素代表一个设备像素,则会加载 elva-fairy-320w.jpg
图像(隐含 1x,因此您无需包含它)。如果设备具有每 CSS 像素两个或更多设备像素的高分辨率,则会加载 elva-fairy-640w.jpg
图像。640px 图像为 93KB,而 320px 图像仅为 39KB。
艺术指导
回顾一下,艺术指导问题是指为了适应不同的图像显示尺寸而希望更改显示的图像。例如,当在桌面浏览器上查看时,网页包含一张带有中间人物的大型风景照片。当在移动浏览器上查看时,同一张图像会被缩小,使得图像中的人物变得非常小且难以看清。最好在移动设备上显示一张较小的肖像图像,该图像会放大人物。 <picture>
元素允许我们实现这种解决方案。
回到我们最初的not-responsive.html示例,我们有一个急需艺术指导的图像
<img src="elva-800w.jpg" alt="Chris standing up holding his daughter Elva" />
让我们用<picture>
来解决这个问题!像<video>
和<audio>
一样,<picture>
元素是一个包装器,包含几个<source>
元素,它们为浏览器提供了不同的来源供选择,然后是至关重要的<img>
元素。responsive.html中的代码如下
<picture>
<source media="(width < 800px)" srcset="elva-480w-close-portrait.jpg" />
<source media="(width >= 800px)" srcset="elva-800w.jpg" />
<img src="elva-800w.jpg" alt="Chris standing up holding his daughter Elva" />
</picture>
<source>
元素包含一个带有媒体条件的media
属性——就像第一个srcset
示例一样,这些条件是决定显示哪个图像的测试——第一个返回true的图像将被显示。在这种情况下,如果视口宽度小于800像素,则显示第一个<source>
元素的图像。如果视口宽度为800像素或更大,则显示第二个。srcset
属性包含要显示的图像路径。正如我们上面看到的<img>
,<source>
可以带一个引用多个图像的srcset
属性,以及一个sizes
属性。因此,您可以通过<picture>
元素提供多个图像,但也可以为每个图像提供多个分辨率。实际上,您可能不会经常这样做。- 在所有情况下,您都必须在
</picture>
之前提供一个带有src
和alt
的<img>
元素,否则不会显示任何图像。这提供了一个默认情况,适用于所有媒体条件都不为真时(实际上您可以删除此示例中的第二个<source>
元素),以及一个不支持<picture>
元素的浏览器的回退。
此代码允许我们在宽屏和窄屏显示器上显示合适的图像,如下所示
注意:您应该只在艺术指导场景中使用 media
属性;当您使用 media
时,不要在 sizes
属性中也提供媒体条件。
为什么我们不能只用 CSS 或 JavaScript 来实现呢?
当浏览器开始加载页面时,它会在主解析器开始加载和解释页面的 CSS 和 JavaScript 之前开始下载(预加载)任何图像。这种机制通常有助于减少页面加载时间,但对于响应式图像来说却无济于事——因此需要实现 srcset
等解决方案。例如,您无法加载<img>
元素,然后用 JavaScript 检测视口宽度,如果需要的话再动态将源图像更改为较小的图像。到那时,原始图像已经加载完毕,您还会加载小图像,这在响应式图像方面甚至更糟。
实现您自己的响应式图像
在此练习中,我们希望您勇敢地独自完成,大部分。我们希望您使用 <picture>
实现自己的适合艺术指导的窄屏/宽屏截图,并使用 srcset
实现一个分辨率切换示例。
- 编写一些 HTML 来包含您的代码(如果您喜欢,可以使用
not-responsive.html
作为起点)。 - 找到一张漂亮的宽屏风景图片,其中包含一些细节。使用图形编辑器创建其网络尺寸版本,然后将其裁剪以显示放大细节的较小部分,并创建第二张图片(大约 480 像素宽比较合适)。
- 使用
<picture>
元素实现艺术指导图片切换器! - 创建不同尺寸的多个图像文件,每个文件显示相同的图片。
- 使用
srcset
/sizes
创建一个分辨率切换示例,无论是根据设备分辨率以不同分辨率提供相同尺寸的图像,还是根据视口宽度提供不同尺寸的图像。
总结
响应式图像的讲解到此结束——我们希望您喜欢尝试这些新技术。回顾一下,我们在这里讨论了两个截然不同的问题
另见
- 学习:响应式设计
- Jason Grigsby 对响应式图像的精彩介绍
- 响应式图片:如果您只是更改分辨率,请使用 srcset——包含更多关于浏览器如何确定使用哪个图像的解释
<img>
<picture>
<source>