Learn web development

Responsive images

在这篇文章中我们将学习关于响应式图片——一种可以在不同的屏幕尺寸和分辨率的设备上都能良好工作以及其他特性的图片——的内容,并且看看HTML提供了什么工具来帮助实现它们。响应式图片仅仅只是响应式web设计的一部分(奠定了响应式web设计的良好基础),我们会在未来的CSS topic模块中学到更多关于这一主题的知识。

Prerequisites: You should already know the basics of HTML and how to add static images to a webpage.
Objective: Learn how to use features like srcset and the <picture> element to implement responsive image solutions on websites.

为什么要用自适应的图片?

我们会遇到什么样的问题需要用响应式图片来解决呢?让我们看看一个典型的场景。一个典型的网站可能会有一张页眉图片让访问者看起来感到愉快。可能会在图片下面添加一些内容图像。你可能像让头部(header)能跨越页眉(header)的全部宽度。并且内容图像适合内容纵列的某处。让我们看一个简单的例子:

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.

这个网页在宽屏设备上表现良好,例如笔记本电脑或台式机(你可以see the example live 并且在GitHub上查看 source code )。我们不会过多的讨论CSS,我们只能说:

  • 正文内容被设置的最大宽度为1200像素—在高于该宽度的视口中,正文保持在1200像素,并将其本身置于可用空间中。在该宽度以下的视窗中,正文将保持在视窗宽度的100%。
  • 页眉图像已设置为使其中心始终位于头部的中心,无论头部的宽度是多少。所以如果网站被显示在窄屏上,图片中心的重要细节(里面的人)仍然可以看到,而两边超出的部分都消失了。它的高度是200px。
  • 内容图片已经被设置为如果body元素比图像更小,图像就开始缩小,这样图像总是在正文里,而不是溢出正文。

虽然这还能看,但是当你尝试在一个狭小的屏幕设备上查看本页面时,网页的头部看起来还可以,但是头部这张图片占据了屏幕的一大部分的高度,这真的很糟糕啊,而且这种情况下你仅仅只能看到人,旁边的树少了很多。

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.

当网站在狭窄的屏幕上观看时,会显示一幅图片的裁剪版本,裁剪版本有重要的细节,而对于像平板电脑这样的中等宽度的屏幕设备来说,也许是两者之间的东西—这通常被称之为艺术方向问题(art direction problem)

另外,如果是在小屏手机屏幕上显示网页,那么没有必要在网页上嵌入这样大的图片。这被称之为分辨率切换问题(resolution switching problem)—一张位图被设置为固定像素的宽和高。当我们看矢量图形时,我们会看到当位图大于其原始的尺寸时开始变得模糊(矢量图形不会变模糊)。如果位图的显示尺寸比原始尺寸小,就会浪费带宽——当可以使用小图片用于设备时,手机用户尤其不愿意通过下载用于桌面的大图像浪费带宽。理想的情况是当访问网站时依靠不同的设备来提供不同的分辨率图片和不同尺寸的图片。

让事情变得复杂的是,有些设备有很高的分辨率,为了显示的更出色,可能需要超出你预料的更大的图像。这从本质上是一样的问题,但在环境上有一些不同。

你可能会认为矢量图形能解决这些问题,在某种程度上是这样的——它们无论是文件大小还是比例都合适,无论在哪里你都应该尽可能的使用它们。然而,它们并不适合所有的图片类型,虽然在简单图形、图案、界面元素等方面较好,但如果是有大量的细节的照片,创建矢量图像会变得非常复杂。像JPEG格式这样的位图会更适合上面例子中的图像。

当web第一次出现时,这样的问题并不存在,在上世纪90年代中期,仅仅可以通过笔记本电脑和台式机来浏览web页面,所以浏览器开发者和规范制定者甚至没有想到要实现这种解决方式(响应式开发)。最近应用的响应式图像技术,通过让浏览器提供多个图像文件来解决上述问题,或者显示相同尺寸的图片但包含不同的分辨率(分辨率切换),又或者使用不同的图片适应不同的空间分配(艺术方向)。

Note: 在这篇文章中讨论的新特性 — srcset/sizes/<picture> — 都已经被新版本的现代浏览器和移动浏览器所支持(包括Edge,而不是IE)。

怎样创建自适应的图片?

在这一部分中,我们将看看上面说明的两个问题,并且展示怎样用HTML的响应式图片来解决这些问题。你应该注意到,我们将专注于本节HTML的 <img>,就是在上面例子中的内容图片,网站页眉的图片仅仅用于装饰,因此使用CSS的背景图片来实现。CSS是比HTML更好的响应式设计的工具,我们会在未来的CSS模块中讨论。

分辨率切换:不同的尺寸

那么,我们想要用分辨率切换解决什么问题呢?我们想要显示相同的图片内容,仅仅依靠设备来显示更大或更小的图片——这是我们在示例中使用第二个内容图像的情况。标准的<img>元素传统上仅仅让你给浏览器指定唯一的资源文件。

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

然而我们可以使用两个新的属性——srcset 和 sizes——可以提供更多额外的资源图像和提示来帮助浏览器选择正确的一个资源。你可以看到 reponsive.html 的例子,也可以在GitHub上看到source code

<img srcset="elva-fairy-320w.jpg 320w,
             elva-fairy-480w.jpg 480w,
             elva-fairy-800w.jpg 800w"
     sizes="(max-width: 320px) 280px,
            (max-width: 480px) 440px,
            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:480px))——你会在 CSS topic中学到更多的。但是现在我们仅仅讨论的是媒体条件描述了屏幕可能处于的状态。在这里,我们说“当视窗的宽度是480像素或更少”。
  2. 一个空格
  3. 当媒体条件为真时,图像将填充的槽的宽度440px

Note: For the slot width, you may provide an absolute length (px, em) or a relative length (such as a percentage.) You may have noticed that the last slot width has no media condition — this is the default that is chosen when none of the media conditions are true.) The browser ignores everything after the first matching condition, so be careful how you order the media conditions.

所以,有了这些属性,浏览器会:

  1. Look at its device width.查看设备宽度
  2. 检查sizes列表中哪个媒体条件是第一个为真
  3. 查看给予该媒体查询的槽大小
  4. 加载srcset列表中引用的最接近所选的槽大小的图像

就是这样!所以在这里,如果支持浏览器以视窗宽度为480px来加载页面,那么(max-width: 480px)的媒体条件为真,因此440px的槽会被选择,所以elva-fairy-480w.jpg将加载,因为它的的固定宽度(480w)最接近于440px。800px的照片大小为128KB而480px版本仅有63KB大小—节省了65KB。现在想象一下,如果这是一个有很多图片的页面。使用这种技术会节省移动端用户的大量带宽。

老旧的浏览器不支持这些特性,它会忽略这些特征。并继续正常加载 src属性引用的图像文件。

Note: In the <head> of the document you'll find the line <meta name="viewport" content="width=device-width">: this forces mobile browsers to adopt their real viewport width for loading web pages (some mobile browsers lie about their viewport width, and instead load pages at a larger viewport width then shrink the loaded page down, which is not very helpful for responsive images or design. We'll teach you more about this in a future module.)

一些有用的开发工具

There are some useful developer tools in browsers to help with working out the necessary slot widths, etc, that you need to use. When I was working them out, I first loaded up the non-responsive version of my example (not-responsive.html), then went into Responsive Design View (Tools > Web Developer > Responsive Design View), which allows you to look at your web page layouts as if they were being viewed through a variety of different device screen sizes.

I set the viewport width to 320px then 480px; for each one I went into the DOM Inspector, clicked on the <img> element we are interested in, then looked at its size in the Box Model view tab on the right hand side of the display. This should give you the inherent image widths you need.

A screenshot of the firefox devtools with an image element highlighted in the dom, showing its dimensions as 440 by 293 pixels.

Next, you can check whether the srcset is working by setting the viewport width to what you want (set it to a narrow width, for example), opening the Network Inspector (Tools > Web Developer > Network), then reloading the page. This should give you a list of the assets that were downloaded to make up the webpage, and here you can check which image file was chosen for download.

a screenshot of the network inspector in firefox devtools, showing that the HTML for the page has been downloaded, along with three images, which include the two 800 wide versions of the responsive images

分辨率切换: 相同的尺寸, 不同的分辨率

如果你支持多分辨率显示,但每个人在屏幕上看到的图片的实际尺寸是相同的,你可以让浏览器通过srcset和x-descriptors但没有sizes——一种更简单的语法——来选择适当的分辨率图片。你可以看一个例子 srcset-resolutions.html (还有 the source code):

<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会应用在图片上,所以它的宽度在屏幕上是320像素(也称作CSS像素):

img {
  width: 320px;
}

在这种情况下,sizes并不需要——浏览器只是计算出正在显示的显示器的分辨率,然后提供srcset引用的最适合的图像。因此,如果访问页面的设备具有标准/低分辨率显示,一个设备像素表示一个CSS像素,elva-fairy-320w.jpg会被加载(1x 是隐藏的,所以你不需要写出来)。如果设备有高分辨率,两个或更多的设备像素表示一个CSS像素,the 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> and <audio>,<picture>元素包含了一些<source>元素,它使浏览器在不同来源之间做出选择,紧跟着的是重要的<img>元素。responsive.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例子,这些条件来决定哪一个图片会显示——第一个条件返回真,那么就会显示这张图片。在这种情况下,如果视窗的宽度为799px或更少,第一个<source>元素的图片就会显示。如果视窗的宽度是800px或更大,就显示第二张图片。
  • srcset属性包含要显示图片的路径。请注意,正如我们在<img>上面看到的那样,<source>可以使用引用多个图像的srcset属性,还有sizes属性。所以你可以通过一个 <picture>元素提供多个图片,不过也可以给每个图片提供多分辨率的图片。实际上,你可能不想经常做这样的事情。
  • 在所有的情况下,你都必须在 </picture>之前正确提供一个<img>元素以及它的srcalt属性,否则不会有图片显示。当媒体条件都不返回真的时候(你可以在这个例子中删除第二个<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

Note: 你应该仅仅当在艺术方向场景下使用media属性;当你使用media时,不要在sizes属性中也提供媒体条件。

Why can't we just do this using CSS or JavaScript?

When the browser starts to load a page, it starts to download (preload) any images before the main parser has started to load and interpret the page's CSS and JavaScript. This is a useful technique, which on average has shaved 20% off page load times. However, it is not helpful for responsive images, hence the need to implement solutions like srcset. You couldn't for example load the <img> element, then detect the viewport width with JavaScript and dynamically change the source image to a smaller one if desired. By then, the original image would already have been loaded, and you would load the small image as well, which is even worse in responsive image terms.

大胆的使用现代图像格式

有很多令人激动的新图像格式(例如WebP和JPEG-2000)可以在有高质量的同时有较低的文件大小。然而,浏览器对其的支持参差不起。

<picture>让我们能继续满足老式浏览器的需要。你可以在type属性中提供MIME类型,这样浏览器就能立即拒绝其不支持的文件类型:

<picture>
  <source type="image/svg+xml" srcset="pyramid.svg">
  <source type="image/webp" srcset="pyramid.webp">
  <img src="pyramid.png" alt="regular pyramid built from four equilateral triangles">
</picture>
  • 不要使用media属性,除非你也需要艺术方向。
  • .在<source> 元素中,你只可以引用在type中声明的文件类型。
  • 像之前一样,如果必要,你可以在srcset和sizes中使用逗号分割的列表。

Active learning: Implementing your own responsive images

For this active learning, we're expecting you to be brave and go it alone ... mostly. We want you to implement your own suitable art directed narrow screen/wide screen shot using <picture>, and a resolution switching example that uses srcset.

  1. Write some simple HTML to contain your code (use not-responsive.html as a starting point, if you like)
  2. Find a nice wide screen landscape image with some kind of detail contained in it somewhere. Create a web-sized version of it using a graphics editor, then crop it to show a smaller part that zooms in on the detail, and create a second image (about 480px wide is good for this.)
  3. Use the <picture> element to implement an art direction picture switcher!
  4. Create multiple image files of different sizes, each showing the same picture.
  5. Use srcset/size to create a resolution switcher example, either to serve the same size image at different resolutions, or different image sizes at different viewport widths.

Note: Use the browser devtools to help work out what sizes you need, as mentioned above.

Summary

That's a wrap for responsive images — we hope you enjoyed playing with these new techniques. As a recap, there are two distinct problems we've been discussing here:

  • Art direction: The problem whereby you want to serve cropped images for different layouts — for example a landscape image showing a full scene for a desktop layout, and a portrait image showing the main subject zoomed in close for a mobile layout. This can be solved using the <picture> element.
  • Resolution switching: The problem whereby you want to serve smaller image files to narrow screen devices, as they don't need huge images like desktop displays do — and also optionally that you want to serve different resolution images to high density/low density screens. This can be solved using vector graphics (SVG images), and the srcset and sizes attributes.

This also draws to a close the entire Multimedia and embedding module! The only thing to do now before moving on is to try our multimedia assessment, and see how you get on. Have fun.

See also

文档标签和贡献者