响应式图片

在本文中,我们将学习响应式图片的概念——在屏幕尺寸、分辨率和其他功能差异很大的设备上都能良好显示的图片——并了解 HTML 提供的哪些工具可以帮助实现它们。这有助于提高不同设备上的性能。响应式图片只是 响应式设计 的一部分,这是您将来要学习的 CSS 主题。

先决条件 您应该已经了解 HTML 基础 以及如何 向网页添加静态图像
目标 学习如何使用 srcset<picture> 元素等功能在网站上实现响应式图像解决方案。

为什么需要响应式图片?

让我们来看一个典型的场景。一个典型的网站可能包含一个标题图像和标题下方的一些内容图像。标题图像可能会横跨标题的整个宽度,内容图像将适合内容列中的某个位置。这是一个简单的示例

Our example site as viewed on a wide screen - here the first image works OK, as it is big enough to see the detail in the center.

这在宽屏设备(例如笔记本电脑或台式机)上效果很好(您可以 查看示例 并找到 GitHub 上的 源代码)。在本课中,我们不会过多讨论 CSS,只是说

  • 主体内容的最大宽度设置为 1200 像素——在超过该宽度的视口中,主体保持 1200px 并居中显示在可用空间中。在低于该宽度的视口中,主体将保持视口宽度的 100%。
  • 标题图像的设置使得无论标题的宽度设置为多少,其中心始终保持在标题的中心。如果网站在较窄的屏幕上查看,则图像中心的重要细节(人物)仍然可见,多余的部分则会丢失在两侧。它的高度为 200px。
  • 内容图像的设置使得如果主体元素变得小于图像,则图像开始缩小,以便始终保持在主体内部,而不是溢出主体。

但是,当您开始在窄屏设备上查看网站时,就会出现问题。下面的标题看起来还可以,但它开始占据移动设备大量屏幕高度。而且在这个尺寸下,很难看清第一个内容图像中两个人的脸。

Our example site as viewed on a narrow screen; the first image has shrunk to the point where it is hard to make out the detail on it.

改进的方法是在网站在窄屏上查看时,显示图像的裁剪版本,显示图像的重要细节。可以在平板电脑等中等宽度屏幕设备上显示第二个裁剪图像。以这种方式为各种布局提供不同裁剪图像的通用问题通常称为艺术指导问题

此外,如果在移动设备屏幕上查看页面,则无需在页面上嵌入如此大的图像。这样做会浪费带宽;特别是,移动用户不想浪费带宽来下载专为桌面用户设计的图像,而一个小图像足以满足其设备的需求。相反,当一个小型的光栅图像显示尺寸大于其原始尺寸时,它会开始变得模糊(光栅图像的宽度和高度都是固定像素数,我们在查看矢量图形时已经看到了)。理想情况下,会向用户的 Web 浏览器提供多种分辨率。然后,浏览器可以根据用户设备的屏幕尺寸确定要加载的最佳分辨率。这被称为分辨率切换问题

为了使事情变得更加复杂,某些设备具有高分辨率屏幕,需要比您预期的更大的图像才能很好地显示。这本质上是相同的问题,但处于略微不同的上下文中。

您可能会认为矢量图像可以解决这些问题,并且在某种程度上确实如此——它们的 文件大小很小并且可以很好地缩放,并且您应该尽可能地使用它们。但是,它们不适用于所有图像类型。矢量图像非常适合简单的图形、图案、界面元素等,但是使用矢量图像创建具有照片中那种细节的图像会变得非常复杂。JPEG 等光栅图像格式更适合我们在上述示例中看到的图像类型。

在 90 年代初期到中期,网络刚刚出现时,这种问题并不存在——当时唯一存在的用于浏览网络的设备是台式机和笔记本电脑,因此浏览器工程师和规范编写者甚至没有考虑过实现解决方案。响应式图像技术最近被实施以解决上述问题,方法是允许您为浏览器提供多个图像文件,这些文件要么都显示相同的内容但包含不同数量的像素(分辨率切换),要么是适合不同空间分配的不同图像(艺术指导)。

注意:本文讨论的新功能——srcset/sizes/<picture>——都受现代桌面和移动浏览器支持。

如何创建响应式图片?

在本节中,我们将研究上面说明的两个问题,并展示如何使用 HTML 的响应式图像功能来解决它们。您应该注意,我们将重点关注本节中的<img>元素,如上例的内容区域所示——站点标题中的图像仅用于装饰,因此使用 CSS 背景图像实现。CSS 在响应式设计方面拥有比 HTML 更好的工具,我们将在以后的 CSS 模块中讨论这些工具。

分辨率切换:不同尺寸

那么,我们希望通过分辨率切换解决什么问题呢?我们希望显示相同的图像内容,只是根据设备的大小显示更大或更小——这就是我们示例中第二个内容图像的情况。传统的<img>元素通常只允许您将浏览器指向单个源文件。

html
<img src="elva-fairy-800w.jpg" alt="Elva dressed as a fairy" />

但是,我们可以使用两个属性——srcsetsizes——来提供几个额外的源图像以及帮助浏览器选择合适的图像的提示。您可以在 GitHub 上的responsive.html示例中看到一个示例(另请参阅源代码

html
<img
  srcset="elva-fairy-480w.jpg 480w, elva-fairy-800w.jpg 800w"
  sizes="(max-width: 600px) 480px,
         800px"
  src="elva-fairy-800w.jpg"
  alt="Elva dressed as a fairy" />

srcsetsizes属性看起来很复杂,但是如果您像上面显示的那样格式化它们,每个属性值都放在一行上,那么它们并不难理解。每个值都包含一个逗号分隔的列表,这些列表的每个部分都由三个子部分组成。现在让我们浏览一下每个部分的内容。

srcset定义了我们将允许浏览器从中选择的图像集,以及每个图像的大小。每个图像信息集都用逗号与前一个图像信息集分隔开。对于每个图像信息集,我们都会编写

  1. 一个图像文件名elva-fairy-480w.jpg
  2. 一个空格
  3. 图像的固有宽度(以像素为单位)480w)——请注意,这里使用的是w单位,而不是您可能预期的px。图像的固有尺寸是其真实尺寸,可以通过检查计算机上的图像文件找到(例如,在 Mac 上,您可以选择 Finder 中的图像并按Cmd + I调出信息屏幕)。

sizes定义了一组媒体条件(例如屏幕宽度),并在某些媒体条件为真时指示选择什么图像尺寸最合适——这些是我们之前讨论过的提示。在这种情况下,在每个逗号之前,我们都会编写

  1. 一个媒体条件(max-width:600px))——您将在CSS 主题中了解更多关于这些内容,但现在让我们假设媒体条件描述了屏幕可能处于的某个状态。在这种情况下,我们说的是“当视口宽度为 600 像素或更小时”。
  2. 一个空格
  3. 媒体条件为真时图像将填充的插槽宽度480px

注意:sizes中,您可以使用任何长度值。例如,您可以提供相对于视口的宽度(例如50vw),而不是提供绝对宽度(例如480px)。但是,您不能使用百分比作为插槽宽度。您可能已经注意到,最后一个插槽宽度没有媒体条件(这是在没有媒体条件为真的情况下选择的默认值)。浏览器会忽略第一个匹配条件之后的所有内容,因此请注意媒体条件的顺序。

因此,有了这些属性,浏览器将

  1. 查看屏幕尺寸、像素密度、缩放级别、屏幕方向和网络速度。
  2. 确定sizes列表中哪个媒体条件是第一个为真的条件。
  3. 查看给定媒体查询的插槽大小。
  4. 加载srcset列表中与插槽大小相同的图像,或者如果没有,则加载第一个大于所选插槽大小的图像。

就是这样!此时,如果具有 480px 视口宽度的支持浏览器加载页面,则(max-width: 600px)媒体条件将为真,因此浏览器会选择480px插槽。elva-fairy-480w.jpg将被加载,因为其固有宽度(480w)最接近插槽大小。800 像素的图片在磁盘上的大小为 128KB,而 480 像素的版本仅为 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 描述符但不带sizessrcset允许浏览器选择适当的分辨率图像——这是一种稍微简单的语法!您可以在srcset-resolutions.html中找到一个示例(另请参阅源代码

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" />

请注意,即使图像始终以相同的大小显示,在高分辨率显示器上,您也可以看到更多细节。

A picture of a little girl dressed up as a fairy, with an old camera film effect applied to the image

在此示例中,以下 CSS 应用于图像,以便它在屏幕上(也称为 CSS 像素)的宽度为 320 像素。

css
img {
  width: 320px;
}

在这种情况下,不需要sizes——浏览器会计算出显示器正在使用的分辨率,并提供srcset中引用的最合适的图像。因此,如果访问页面的设备具有标准/低分辨率显示器,其中一个设备像素代表每个 CSS 像素,则将加载elva-fairy-320w.jpg图像(1x 是隐含的,因此您无需包含它)。如果设备具有高分辨率,每个 CSS 像素有两个或更多设备像素,则将加载elva-fairy-640w.jpg图像。640 像素的图像为 93KB,而 320 像素的图像仅为 39KB。

艺术指导

概括地说,艺术指导问题涉及希望更改显示的图像以适应不同的图像显示尺寸。例如,网页在桌面浏览器上查看时包含一个中间有人物的大型横向照片。在移动浏览器上查看时,同一图像会缩小,使图像中的人物变得非常小且难以看到。在移动设备上显示较小的纵向图像可能更好,该图像会放大人物。<picture>元素允许我们实现这种类型的解决方案。

回到我们最初的not-responsive.html示例,我们有一个非常需要艺术指导的图像。

html
<img src="elva-800w.jpg" alt="Chris standing up holding his daughter Elva" />

让我们用<picture>来修复它!与<video><audio>一样,<picture>元素是一个包装器,包含多个<source>元素,这些元素为浏览器提供了多个源以供选择,然后是至关重要的<img>元素。responsive.html中的代码如下所示。

html
<picture>
  <source media="(max-width: 799px)" srcset="elva-480w-close-portrait.jpg" />
  <source media="(min-width: 800px)" srcset="elva-800w.jpg" />
  <img src="elva-800w.jpg" alt="Chris standing up holding his daughter Elva" />
</picture>
  • <source>元素包含一个media属性,该属性包含一个媒体条件——与第一个srcset示例一样,这些条件是决定显示哪个图像的测试——第一个返回 true 的图像将被显示。在这种情况下,如果视口宽度为 799 像素或更小,则将显示第一个<source>元素的图像。如果视口宽度为 800 像素或更大,则将显示第二个元素的图像。
  • srcset属性包含要显示的图像的路径。就像我们在上面看到的<img>一样,<source>可以采用srcset属性,其中引用了多个图像,以及sizes属性。因此,您可以通过<picture>元素提供多个图像,然后也为每个图像提供多个分辨率。实际上,您可能不会经常想要做这种事情。
  • 在所有情况下,您都必须在</picture>之前提供一个带有srcalt<img>元素,否则将不会显示任何图像。这提供了一个默认情况,当没有媒体条件返回 true 时将应用此情况(您实际上可以从此示例中删除第二个<source>元素),以及不支持<picture>元素的浏览器的后备方案。

此代码允许我们在宽屏和窄屏显示器上显示合适的图像,如下所示。

Our example site as viewed on a wide screen - here the first image works OK, as it is big enough to see the detail in the center. Our example site as viewed on a narrow screen with the picture element used to switch the first image to a portrait close up of the detail, making it a lot more useful on a narrow screen

注意:您应该仅在艺术指导场景中使用media属性;当您使用media时,不要在sizes属性中也提供媒体条件。

为什么我们不能只使用 CSS 或 JavaScript 来做到这一点?

当浏览器开始加载页面时,它会开始下载(预加载)任何图像,然后主解析器才开始加载和解释页面的 CSS 和 JavaScript。该机制通常用于减少页面加载时间,但对响应式图像没有帮助——因此需要实现像srcset这样的解决方案。例如,您不能加载<img>元素,然后使用 JavaScript 检测视口宽度,然后根据需要动态地将源图像更改为较小的图像。到那时,原始图像已经加载,您还将加载小图像,这在响应式图像方面甚至更糟糕。

主动学习:实现您自己的响应式图片

对于此主动学习,我们希望您勇敢地独自完成,主要由您自己完成。我们希望您使用<picture>实现您自己的合适的艺术指导窄屏/宽屏截图,以及使用srcset的分辨率切换示例。

  1. 编写一些简单的 HTML 来包含您的代码(如果需要,可以使用not-responsive.html作为起点)。
  2. 找到一个漂亮的宽屏横向图像,其中包含某种细节。使用图形编辑器创建该图像的网页版本,然后裁剪它以显示放大细节的较小部分,并创建第二个图像(大约 480 像素宽适合此)。
  3. 使用<picture>元素实现艺术指导图片切换器!
  4. 创建多个不同尺寸的图像文件,每个文件都显示相同的图片。
  5. 使用srcset/sizes创建分辨率切换器示例,或者根据设备分辨率提供相同尺寸图像的不同分辨率,或者根据视口宽度提供不同图像尺寸。

总结

响应式图像到此结束——我们希望您喜欢尝试这些新技术。作为回顾,我们在这里讨论了两个不同的问题。

  • 艺术指导:您希望为不同的布局提供裁剪图像的问题——例如,横向图像为桌面布局显示完整场景,纵向图像为移动布局显示放大的主要主题。您可以使用<picture>元素解决此问题。
  • 分辨率切换:当您希望为窄屏设备提供更小的图像文件时,就会遇到这个问题,因为它们不需要像桌面显示器那样的大图像——以及为高密度/低密度屏幕提供不同分辨率的图像。您可以使用矢量图形(SVG 图像)以及带有srcsetsizes属性的srcset来解决此问题。

这也标志着整个多媒体和嵌入模块的结束!现在,在继续学习之前,您只需要尝试我们的多媒体和嵌入评估,看看您的学习成果。祝您玩得开心!

另请参阅