聊聊img元素
图像是Web中最常见的媒体属性之一,对于开发者而言,都知道使用HTML的<img>
元素或CSS的background-image
属性可以将图像放到Web上,但更多的时候都在聊,如何使用CSS相关的属性来处理Web中的图像,比如使用图像的适配、图像效果的处理等。事实上,HTML中的<img>
元素也有很多属性对于我们在Web中使用图像起着重要的作用,特别是随着前端技术革新,<img>
新增了一些新的属性,比如srcset
、sizes
、loading
和intrinsicsize
等。
在这篇文章中,主要想和大家聊聊HTML的<img>
中这些新增的属性对Web图像带来哪些方面的变化。如果你对CSS如何处理Web图像相关的知识感兴趣的话,可以阅读下面这些文章:
img元素的属性
HTML的<img>
元素有很多个属性,有一些属性是不怎么常用的,甚至从未使用过,比如crossorigin
、decoding
、importance
等;有些属性是常用的,比如src
、alt
、width
、height
、title
等;有些属性是在HTML5中新增加的,比如srcset
、sizes
、loading
、intrinsicsize
等。其中有的属性是使用<img>
必用的属性,比如 src
,有些是用来优化性能的,比如loading
,而且HTML中还有很多 全局属性 也可以运用于该元素上。
下面和大家一起聊聊其中一些有意思的属性。
指定图像源:src
Web开发者都知道使用<img>
元素可以把图像放到Web页面或Web应用上。它是一个空元素(它不需要包含文本内容或闭合标签),只不过至少需要一个 src
属性来使用其生效。src
属性包含了指向我们想要引入的图像路径,可以是相对路径或绝对路径,就像<a>
元素的href
属性一样。
来看个例子,比如说你想把一个文件名为grapefruit-slice-332-332.jpg
的图像,且它与你的HTML页面存放在相同的路径下,那么你可以像下面这样将图像嵌入到页面中:
<img src="grapefruit-slice-332-332.jpg" />
不过,在构建一个项目的时候,图像一般会放到一个文件目录为images
下,那么引用的时候就像下面这样:
<img src="images/grapefruit-slice-332-332.jpg" />
上面示例中src
采用的是相对路径方式,你也可以使用一个绝对路径地址,比如:
<img src="https://static.fedev.cn/sites/default/files/blogs/2020/2003/grapefruit-slice-332-332.jpg" />
但是这种方式是不被推荐的,这样做只会使浏览器做更多的工作,例如重新通过DNS
再去寻找IP
地址。通常我们都会把图像和HTML放在同一个服务器上。
很多时候会把图像存放在
CDN
,也可得到一个绝对路径。
如果图片加载没有出问题的话,就会看到你想的图片效果,如果加载失败的话,会有一个缩略图标(不同的浏览器看到的效果会有所不同):
如果在加载或渲染图像时发生错误,且设置了至少一个 onerror
事件处理器来处理 error
事件,那么设置的事件处理器就会被调用。这样的错误可能发生在各种不同的情况下,包括:
src
属性的属性值为空(""
)或者null
src
属性的URL
和用户正在浏览的页面的URL
完全相同- 图像出于某些原因损坏了,从而无法加载
- 图像的元数据被破坏了,无法检索它的分辨率/宽高,而且在
<img>
元素的属性中没有指定宽度和/或高度 - 用户代理尚未支持该图片所用的格式
在使用src
给<img>
指定加载的图片时,有的时候会使用JavaScript动态脚本来给src
指定值,有的时候可能会给src
指定一个空的或一些字符,比如像下面这样的一个示例:
<img src="https://static.fedev.cn/sites/default/files/blogs/2020/2003/grapefruit-slice-332-332.jpg" alt="" />
<img src="#" alt="" />
<img src="" alt="" />
在这种情况下的时候,对于src="#"
来说,也会发生一个HTTP
请求,而对于src=""
不会发生任何的请求,如下所示:
注意,不同的浏览器下有所差异
添加备选文案:alt
接下来讨论的是alt
属性。上面的示例中你可能发现了,图片加载失败之后不同的浏览器渲染出来的效果会有所不同。而我们使用img
将图像嵌入到页面中时,很多时候是因为图像也是Web的一个重要信息。因此,就算是图片加载失败了,我们也希望用户能获取到相关的信息。这个时候我们就需要使用img
的另一个重要属性,即 alt
属性。该属性的值是用来描述图像相关的信息或者用来描述图像在Web中上下文的相关信息。比如上面的示例我们可以修改成:
<img src="https://static.fedev.cn/sites/default/files/blogs/2020/2003/grapefruit-slice-332-332.jpg" alt="图像" />
<img src="#" alt="图像" />
<img src="" alt="图像" />
图像加载失败或不能正常显示的时候,浏览器就会把alt
指定的值显示出来,只不过不同的浏览器下显示效果有所差异:
注意:无法使用CSS来对
img
加载失败时设置alt
在浏览器中显示效果。
那么为什么要使用alt
给img
指定上下文信息呢?那是因为它可以派上用场的原因很多:
- 用户有视力障碍,通过屏幕阅读器来浏览网页 。事实上,给图片一个备选的描述文本对大多数用户都是很有用的
- 就像上面所说的,你也许会把图片的路径或文件名拼错
- 浏览器不支持该图片类型。某些用户仍在使用纯文本的浏览器,例如 Lynx,这些浏览器会把图片替换为描述文本
- 你会想提供一些文字描述来给搜索引擎使用,例如搜索引擎可能会将图片的文字描述和查询条件进行匹配
- 用户关闭的图片显示以减少数据的传输,这在手机上是十分普遍的,并且在一些国家带宽有限且昂贵
你到底应该在 alt
里写点什么呢?这首先取决于为什么这张图片会在这儿,换句话说,如果这张图片没显示出来,会少了什么:
- 装饰:如果图片只是用于装饰,而不是内容的一部分,可以写一个空的
alt=""
。例如,屏幕阅读器不会浪费时间对用户读出不是核心需要的内容。实际上装饰性图片就不应该放在HTML文件里, CSS 的background-image
才应该用于插入装饰图片,但如果这种情况无法避免,alt=""
会是最佳处理方案 - 内容:如果你的图片提供了重要的信息,就要在
alt
文本中简要的提供相同的信息,甚至更近一步,把这些信息写在主要的文本内容里,这样所有人都能看见。不要写冗余的备选文本(如果在主要文本中将所有的段落都重复两遍,对于没有失明的用户来说多烦啊!),如果在主要文本中已经对图片进行了充分的描述,写alt=""
就好 - 链接:如果你把图片嵌套在
<a>
标签里,来把图片变成链接,那你还必须提供无障碍的链接文本。在这种情况下,你可以写在同一个<a>
元素里,或者写在图片的alt
属性里,随你喜欢 - 文本:你不应该将文本放到图像里。例如,如果你的主标题需要有阴影,你可以用CSS来达到这个目的,而不是把文本放到图片里。如果真的必须这么做,那就把文本也放到
alt
里
本质上,关键在于在图片无法被看见时也提供一个可用的体验,这确保了所有人都不会错失一部分内容。记得在A11Y系列教程中的《编写HTML时要考虑可访问性》一文中也特别讨论过,应该如何给alt
添加描述内容。如果你实在不知道怎么给alt
添加内容时,可以按照下图方式来做出相应的选择:
来看一个示例:
比如上图嵌入到Web时,我们就可以像下面这样给alt
添加描述信息:
<!-- 不好的用法: 没有使用alt -->
<img src="bird.png" />
<!-- 一般般的用法:显式设置了`alt`,但是个空值 -->
<img src="bird.png" alt="" />
<!-- 好的用法:alt描述了图片的信息(公鸡),告诉用户这是只公鸡 -->
<img src="bird.png" alt="公鸡" />
<!-- 更好的用法:alt描述了图片的信息(公鸡啼鸣),告诉用户这是只正在啼鸣的公鸡 -->
<img src="bird.png" alt="公鸡啼鸣" />
<!-- 最好的用法:alt描述了图片的信息(红色冠公鸡啼叫),告诉用户这是只正在啼鸣的红色冠公鸡 -->
<img src="bird.png" alt="红色冠公鸡啼鸣" />
在HTML的规范中,也特别的提到过,img
元素代表什么主要取决于 src
属性和alt
属性。
图像标题:title
img
中的title
属性类似于a
链接中的title
属性。可以使用title
给图像提供更进一步的消息。比如:
<img src="https://static.fedev.cn/sites/default/files/blogs/2020/2003/grapefruit-slice-332-332.jpg" alt="切好的血橙" title="产于赣南地区的一种橙子" />
你尝试着把鼠标悬浮在图像上:
正如上图所示,当光标在图像上停下来后会显示出title
属性中指定的值(title
作为提示条呈现给用户)。尽管这样能给用户提供更多的信息,但并不代表所有用户都能看到,比如触摸屏或重度依赖键盘的用户是看不到。另外,title
并不是alt
的替代品。在实际使用的时候,我们要避免直接将alt
的内容复制给title
。这样做可能会让一些屏幕阅读器把同一段描述读两遍,给用户造成一定的困扰。
title
也不应该被用作图像在alt
之外的补充说明信息。如果真的要对图像信息进行补充说明,我们应该将这些信息放到Web中,而不是依附于图像中。我们可以使用HTML5的<figure>
和<figcaption>
给图像增加说明文字。这两个元素也是为此而生的:为图像提供一个语义容器,在标题和图片之间建立清晰的关联。
<figure>
<img src="https://static.fedev.cn/sites/default/files/blogs/2020/2003/grapefruit-slice-332-332.jpg" alt="切好的血橙" />
<figcaption>产于赣南地区的一种橙子</figcaption>
</figure>
在还没有<figure>
和<figcaption>
元素之前,如果希望在可访问性方面做得更到位时,会考虑使用role="figure"
、aria-label
、aria-labelledby
或aria-describedby
,比如上面的示例我们可以写成:
<div role="figure" aria-labelledby="caption">
<img src="https://static.fedev.cn/sites/default/files/blogs/2020/2003/grapefruit-slice-332-332.jpg" alt="切好的血橙" />
<h3 id="caption">产于赣南地区的一种橙子</h3>
</div>
不过有一点需要注意。在<figure>
中使用<img>
有一个较大的误区:很多同学喜欢用<figcaption>
来替代<img>
中的alt
。HTML 5.2的规范中也提到过:
Such cases are to be kept to an absolute minimum. If there is even the slightest possibility of the author having the ability to provide real alternative text, then it would not be acceptable to omit the alt attribute.
另外,<figcaption>
是为了给图像提供标题和摘要,并将其与包含图像的文档联系起来,或者传递附加的信息,这些信息在查看图像本身时可能不太明显。其次,alt
是用来传达图像所代表的重要信息的。<figcaption>
应该提供上下文,以便将图像与主文档联系起来,或者指出需要注意的特定信息。如果一个<figcaption>
代替了alt
,那么这就为有视力的用户创建了重复的信息。还有就是,不正确地使用<figcaption>
替代图像alt
还有其他问题。但是,要识别这些问题,我们需要了解屏幕阅读器一些工作原理。
这里不再对<figure>
和<figcaption>
做更详尽的阐述,如果感兴趣的话,可以花点时间阅读下面这些文章:
图像尺寸:width 和 height
在Web中的任何一图像都有对应的尺寸,了解清楚图像的尺寸大小也不是件易事,比如图像源自身有一个尺寸,<img>
有width
和height
属性,而且CSS也有width
和height
。那么他们之间的关系是什么呢?
首先我们来说一下图像源的尺寸,比如上面示例中的图像,在未加载到Web页面或Web应用中就有一个尺寸,这个尺寸是设计师导出图像的原始尺寸:
如果我们在HTML的img
元素和CSS中都没对该元素显式设置width
或height
(或同时设置),那么浏览器将会识别的是img
的src
引入的图像源的原始尺寸大小:
<img src="https://static.fedev.cn/sites/default/files/blogs/2020/2003/grapefruit-slice-332-332.jpg" alt="切好的血橙" />
我们使用naturalWidth
和naturalHeight
两个API获得图像的原始尺寸。
我们来看另一个场景,在<img>
中显式的设置了width
或height
(或两者同时设置):
<img src="https://static.fedev.cn/sites/default/files/blogs/2020/2003/grapefruit-slice-332-332.jpg" alt="切好的血橙" width="180" />
这个时候<img>
在浏览器渲染出来的width
是在该元素中显式设置的width
,而height
会根据图像的宽高比例做出相应的计算:
在上面示例中,我们采用的是一个正方形图形,我们来换一张不是正方形的图形来看看浏览器对其计算的方式,比如下面这张图:
该图像的原始尺寸是632 x 475
(像素),我们在使用的时候,显式在<img>
中设置了width=300
:
<img src="https://static.fedev.cn/sites/default/files/blogs/2020/2003/cat-632x475.jpg" alt="一只灰色的猫" width="300" />
浏览器渲染出来的尺寸是300 x 225
:
简单地来看看其计算原理。示例中图像的原始尺寸是632 x 475
,即:
› nW = 632
› nH = 475
› R = nW / nH = 632 / 475 = 1.3305263157894738
示例中显式给img
设置了width="300"
,根据上面的公式可以计算出height
的值:
› R = nW / nH
› H = W / R = W / nW * nH
› H = 300 / 632 * 475 = 225.47
上面公式计算出来的结果和浏览器渲染出来的结果是一样的。我们再来看另一情景,显式的在img
中设置height
,比如:
<img src="https://static.fedev.cn/sites/default/files/blogs/2020/2003/cat-632x475.jpg" alt="一只灰色的猫" height="220" />
同样根据上面的公式来推导出width
的值:
› R = nW / nH
› nW = 632
› nH = 475
› w = R * h = nW / nH * h
› w = 632 / 475 * 220 = 292.72
结果和浏览器渲染出来的是一致的,即width="293"
:
很多时候,也会同时显式的给<img>
设置width
和height
,但是,在同时给<img>
设置width
和height
时,如果值不是按照比例设置的话(图像源尺寸比例),你会发现图像会有扭曲的现象:
<img src="https://static.fedev.cn/sites/default/files/blogs/2020/2003/cat-632x475.jpg" alt="一只灰色的猫" width="300" />
<img src="https://static.fedev.cn/sites/default/files/blogs/2020/2003/cat-632x475.jpg" alt="一只灰色的猫" height="180" width="300" />
正如上面的示例所示,如果显式的给<img>
元素同时添加width
和height
时无法确认是否会对图像造成扭曲或变形时,建议给它们设置图像的原始尺寸。
也有很多开发人员选择不给
<img>
显式设置任何尺寸值,那么这样做好不好呢?我们将在后面会和大家一起探讨。
除了在<img>
元素上显式设置width
、height
属性来指定图像大小之外,还可以通过CSS的width
和height
属性指定。我们来看看:
<!-- HTML -->
<img src="https://static.fedev.cn/sites/default/files/blogs/2020/2003/cat-632x475.jpg" alt="一只灰色的猫" />
<img src="https://static.fedev.cn/sites/default/files/blogs/2020/2003/cat-632x475.jpg" alt="一只灰色的猫" height="300" />
<img src="https://static.fedev.cn/sites/default/files/blogs/2020/2003/cat-632x475.jpg" alt="一只灰色的猫" width="300" />
<img src="https://static.fedev.cn/sites/default/files/blogs/2020/2003/cat-632x475.jpg" alt="一只灰色的猫" height="180" width="300" />
// CSS
img {
width: 200px;
}
只是在CSS中显式设置了width:200px
。我们来看一下结果:
从上图的结果我们可以得知,CSS中的width
或height
会覆盖<img>
元素中的width
或height
。如果<img>
元素中未显式的设置width
或height
,那么CSS就会重新计算图像的尺寸。
CSS中只显式给img
设置的width
或height
其中一个属性的话,那么他的计算也会根据图像的宽高比来计算的,比如上例中的第一张图像和第三张图像,最终计算出来的height
是150px
:
› R = nW / nH
› H = W / R = W / nW * nH
› H = 200 / 632 * 475 = 150.316
如果在<img>
元素中显式设置了width
和hegiht
的值,但只在CSS中显式设置了其中一个值的话,有可能会对图像造成扭曲、拉伸等现象,比如上例中的第二张图像。对于这种现象,你又不想去手动计算height
的值,那么可以考虑在CSS中显式设置height:auto
。
img {
width: 200px;
height: auto;
}
CSS中的height:auto
将会覆盖<img>
元素属性height
的值,从而让图像不产生扭曲,拉伸等变形:
这个原理同样的可以用于width
属性值按height
值来做自动计算。
在图解CSS系列的《元素尺寸的设置》中可以得知,在未来给元素设置尺寸大小可能不仅仅使用width
或height
属性了,为了更好的适应于多语言的排版,更多的时候会采用CSS的逻辑属性。
物理属性 | 逻辑属性(horizontal-tab ) |
逻辑属性(vertical-lr ) |
逻辑属性(vertical-rl ) |
---|---|---|---|
width |
inline-size |
block-size |
block-size |
height |
block-size |
inline-size |
inline-size |
min-width |
min-inline-size |
min-block-size |
min-block-size |
min-height |
min-block-size |
min-inline-size |
min-inline-size |
max-width |
max-inline-size |
max-block-size |
max-block-size |
max-height |
max-block-size |
max-inline-size |
max-inline-size |
有一点需要注意,如果在CSS样式中同时出现物理属性和逻辑属性时,那么会根据书写顺序或选择器权重来决定,比如下面这个示例:
img {
width: 200px;
inline-size: 300px; // 会覆盖width
}
// 或
img {
inline-size: 300px;
width: 200px; //会覆盖inline-size
}
我们花了较长的篇幅探讨了怎么给Web上的图像设置尺寸大小。不过大家有没有这样的一个感觉,平时在Web开发中更多的是喜欢通过CSS来设置图像的尺寸大小。事实上,仅仅这样做并不是一个好习惯。随着浏览器在处理<img>
带来的变化,我们在<img>
元素上显式的设置width
和height
属性变得越来越重要。因为这样做能更好的提高用户的体验。接下来,我们花一点时间来聊聊这方面的知识。
在做Web性能优化时时提倡开发者在Web中使用<img>
时,应该显式的给该元素设置width
和height
属性。这样做可以获得最佳性能,从而使页面有适合的图片空间。也可以避免下载图像时布局的移动。我们来看一个示例:
<div class="card">
<h1>这是我家的猫咪</h1>
<img src="https://static.fedev.cn/sites/default/files/blogs/2020/2003/cat-632x475.jpg" alt="一只灰色的猫" />
<p>一只杂毛猫,喵喵.....</p>
</div>
在页面加载图像到图像渲染出来时,页面的布局会有一个移动的过程,如下图所示:
这可能分两个阶段进行渲染,首先在下载HTML时进行渲染,然后在下载图像时进行渲染。使用上面的代码,这将导致主内容在图像下载后跳下来以及div.card
容器由小变大,显示图像所需的空间需要一定的计算时间:
这也给浏览器增加额外的工作,即 图像加载完之后,需要重新计算页面的布局,这样的一个过程会触发重排和重绘的机制。而Web页面上的重排和重绘对于性能影响是极大的。如果,你在同一个Web页面上同时加载多张图像时,那么这个过程就会变得更长,浏览器的压力也会随之变大,Web页面的性能就会更差,从而用户的体验也会更糟糕。
插个题外话,如果你从示接触过浏览器中有关于重排和重绘相关的知识,建议你花点时间阅读下面相关的文章:
- 尽可能减少浏览器重排
- Understanding Repaint and Reflow in JavaScript
- Rendering: repaint, reflow/relayout, restyle【译文】
- 减少页面重排与重绘(Reflow & Repaint)
- 回流与重绘:CSS性能让JavaScript变慢?
- repaint(重绘)和reflow(回流)详解
- 介绍下重绘和回流(Repaint & Reflow),以及如何进行优化
继续回到该话题中来。上面示例的录屏效果中还可以看出存在的另一个不好的体验问题,就是图像渲染完成前后主内容会移动,这对于用户来的阅读体验非常的糟糕,用户有可能需要重新去寻找阅读的位置。
避免这种情况的传统做法是在<img>
元素上显式的设置width
和height
的值。这样即使在没有任何CSS的情况之下,浏览器对该图像的显示也有适当的空间。基于这个,上面的示例可以调整为:
<div class="card">
<h1>这是我家的猫咪</h1>
<img src="https://static.fedev.cn/sites/default/files/blogs/2020/2003/cat-632x475.jpg" alt="一只灰色的猫" width="632" height="475" />
<p>一只杂毛猫,喵喵.....</p>
</div>
页面渲染的效果如下所示:
你可能发现了,图像在还没有渲染出来的时候,在页面中已经有了放置图像的空间,而且图像整个渲染过程中,页面的内容不会移动以及div.card
也不再会由小变大:
即使忽略内容跳跃给用户阅读带来的糟糕体验不说,对CPU
的影响也是相当大的。下图截取的一个简单的测试用例,页面加载了大约100
张图片。左侧显示了<img>
显式设置了width
和height
的结果,右侧显示了<img>
不显式设置width
和height
的结果。
差距还是很大的,特别是在一些低端设备中和较慢的网络环境下,影响会更大。
显式的在
<img>
元素上设置width
和height
能减少页面加载的时间,也能降低CPU占用的量。
前面也提到过,仅仅在HTML中的<img>
元素上显式设置width
或height
有可能是无法达到页面在UI上的效果,因此很多时候会在CSS中重置img
的width
或height
。不过,有的时候在CSS显式设置图像尺寸也是比较繁琐的事情,甚至是会造成一定的问题。比如前面提到的示例中:
<!-- HTML -->
<img src="https://static.fedev.cn/sites/default/files/blogs/2020/2003/cat-632x475.jpg" alt="一只灰色的猫" width="632" height="475" />
// CSS
img {
width: 200px;
}
就会造成图像的扭曲或拉伸:
针对这种现象,在CSS中需要和height:auto
配合使用:
img {
width: 200px;
height: auto;
}
// 当明确指定height值时,width为auto
img {
height: 200px;
width: auto;
}
在Web布局中,有的时候希望图像能跟着容器做相应的调整,这个时候可能会使用max-width/height
来对图像尺寸做限制性的操作:
img {
max-width: 100%;
height: auto;
}
// 或
img {
max-height: 100%;
width: auto;
}
尝试着在容器右下角左右拖动,改变容器大小,你可以看到图像会根据容器大小做出相应的调整,直到图像到了最大尺寸,比如上例中的图像原始宽度是632px
,当容器宽度大于这个值时,那么图像不会再变大。
也就是说,我们在Web中使用图像时:
- 在
<img>
元素上显式设置width
和heihgt
可以分配适合的空间显示图像,避免布局上的跳动 - 在CSS中设置
max-width/height: 100%
和width/height:auto
时,可以重置图像尺寸
这样做,似乎是较为完美了:没有布局的变化,使用CSS也有能力调整图像的大小。不幸的是,当今有的浏览器会存在这样的现象,CSS设置max-width/height: 100%; width/height:auto
时会忽略<img>
元素中设置的width
和height
,页面渲染的时候会出现前面提到的现象:
注意,在CSS中显式设置
width
和height
的值为具体的值,不会有这种现象。
那么问题来了,Web开发者在开发中显式指定图像尺寸时,对于Web布局风险是较大的,有可能会造成图像溢出容器,特别是在面不同的终端设备环境下或者你正在做一个响应式的网站。这或许就是开发者不太喜欢在<img>
元素上显式设置width
和height
值的原因之一吧。或者说,开发者就没有意识到这方面的问题,或者说不太了解我们这里所讨论的问题。
为了解决这个问题,在<img>
中又引入了一个新的属性,就是接下来要聊的intrinsicsize
属性。
忽略图像的实际大小:intrinsicsize
接着上面的现象继续往下聊。
在intrinsicsize
属性还没有出现之前,开发者为了解决这个现象,通过一些CSS的黑魔法来绕开这个现象:图像的宽高比。在CSS根据图像的宽高比来调整图像大小的方法有很多,比如下面文章这些文章中所聊的方法:
- Web中如何实现纵横比
- CSS实现长宽比的几种方案
- 容器长宽比
- Flexible Images
- Aspect Ratios in CSS are a Hack
- Aspect Ratio Calculator
其中最早的一个方案是通过给<img>
添加一个容器,通过padding-top/bottom
的百分比值(根据width
计算)来模拟容器的height
,从而达到容器的高度随宽度变化(即大家所说的宽高比)。比如上面的示例,我们可以调整为:
<!-- HTML -->
<div class="card">
<h1>这是我家的猫咪</h1>
<div class="img__container">
<img src="https://static.fedev.cn/sites/default/files/blogs/2020/2003/cat-632x475.jpg" alt="一只灰色的猫" width="632" height="475" />
</div>
<p>一只杂毛猫,喵喵.....</p>
</div>
// CSS
.img__container {
position: relative;
padding-bottom: 75.1582278481012%; // 475:632 › 475÷632
height: 0;
overflow: hidden;
img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
}
采用图像的宽高比来做,完美的绕开了上面提到的问题,但该方案也有一个明显的缺陷,那就是要在CSS中对宽高比做计算,加上Web中图像的比例不一致,造成代码的复杂性。庆幸的是,CSS工作组提出了一个新的CSS属性aspect-ratio
解决这方面的问题。一旦该属性得到浏览器的支持之后,实现上面示例的效果就会变得简单地多:
img {
width: 100%;
height: auto;
aspect-ratio: 632 / 475;
}
上面我们看到的都是CSS的一些方案。其实Web孵化器社区小组(WICG:Web Incubator Community Group)也提供了一个解决方案,就是<img>
元素新增加的属性intrinsicsize
。
intrinsicsize
属性告诉浏览器忽略图像的实际大小,假装它是属性中指定的大小。这在与“未调整大小的媒体(Unsized-media)”策略相结合时非常有用,因为它允许图像与视窗宽度成比例,而不会导致布局不稳定。
intrinsicsize
属性会覆盖媒体元素(比如img
元素)的实际大小。具体来说,图像将在这些维度上进行光栅化,图像的naturalWidth/naturalHeight
将返回些属性中指定的值。
在实际中,我们可以像下面这样使用:
<img src="https://static.fedev.cn/sites/default/files/blogs/2020/2003/cat-632x475.jpg" alt="一只灰色的猫" width="632" height="475" intrinsicsize="632x475" />
图像中使用intrinsicsize
属性时的行为:
- 如果
<img>
未显式设置width/height
,则图像的尺寸为intrinsicsize
属性指定的尺寸 - 如果在
<img>
上显式设置了width
,那么intrinsicsize
将设置height
来保持长宽比 - 如果在
<img>
上显式设置了width/height
,那么intrinsicsize
属性的值只会影响<img>
元素的naturalWidth/naturalHeight
的值,而不影响图像渲染的尺寸大小
比如下面的示例:
有关于
intrinsicsize
更详细的介绍,还可以点击这里查阅。
在前面我们提到CSS的aspect-ratio
属性来处理图像的宽高比,@Jen Simmons在intrinsicsize
的讨论中提出了一个更为优雅的解决方案:借助CSS的attr()
函数:
<!-- HTML -->
<img src="https://static.fedev.cn/sites/default/files/blogs/2020/2003/cat-632x475.jpg" alt="一只灰色的猫" width="632" height="475" />
// CSS
img {
width: 100%;
height: auto;
aspect-ratio: attr(width) / attr(height);
}
不过,CSS的attr()
函数目前还无法获取到img
元素的width
和height
。如果得到支持的话,那么它就可以像上面那样自动计算图像的宽高比了。同时也就能解决前面所提到的问题。
简单的小结一下,在使用img
时,如果下面四个条件为真,那么可以计算出图像的正确尺寸,而不需要等待图像下载,也不需要改变内容布局:
- 在HTML的
<img>
元素上显式设置了height
- 在HTML的
<img>
元素上显式设置了width
- 在CSS中设置了
img
的width
或height
- 在CSS中设置了
img
的width
或height
的值为auto
如果其中任何一个没有显式设置,那么计算将是不可能的,因此将失败并被忽略,必须等待图像下载。
自适应的图像:srcset
和sizes
响应式Web设计一直有个问题令开发者头痛,那就是图像的适配处理。
但随着技术的革新,有了更多的方案可以让我们在响应式中处理图像的适配处理,比如说根据不同的分辨率加载不同的图像资源。我们可以使用<img>
新增的srcset
和sizes
属性实现该效果。
img
有了srcset
和sizes
属性后,我们可以像下面这样来加载图像资源:
srcset
和sizes
属性看起来很复杂,但是你按照上图所示进行格式化,那么他们并不是很难理解,每一行有同的属性值。先简单的理解srcset
和sizes
含义:
srcset
:定义了我们允许浏览器选择的图像集,以及每个图像的大小sizes
:定义了一组媒体条件并且指明当某些媒体条件为真时,什么样的图片尺寸是最佳选择
来看几个这方面的小示例:
<img
src="cat@2x.jpg"
alt="一只灰色的猫"
srcset="cat@1x.jpg 1x,
cat@2x.jpg 2x,
cat@3x.jpg 3x"
/>
示例中的srcset
根据设备的dpr
来加载不同的图像资源:
- 当设备屏幕的
dpr="1"
时,使用cat@1x.jpg
- 当设备屏幕的
dpr="2"
时,使用cat@2x.jpg
- 当设备屏幕的
dpr="3"
时,使用cat@3x.jpg
- 不支持
srcset
的浏览器将会使用src
加载的图像cat@2x.jpg
srcset
属性常配合w
使用,比如:
<img
src="cat@2x.jpg"
alt="一只灰色的猫"
srcset="cat-s.jpg 320w,
cat-m.jpg 760w,
cat-l.jpg 1200w"
/>
上面示例中的320w
、760w
和1200w
对应的是图像的实际宽度(像素单位),建议不要随意修改图像的尺寸。除此之外,srcset
属性使用w
宽度描述符时,必须配合sizes
属性一起使用。这样可以覆盖更多的情景。
<img
src="cat@2x.jpg"
alt="一只灰色的猫"
sizes="480px"
srcset="cat-s.jpg 320w,
cat-m.jpg 760w,
cat-l.jpg 1200w"
/>
示例中的图像要显示成480px
(sizes
指定的值),而在srcset
设置了图像的临界值:[0, 320px]
、[320px, 760px]
、[760px, 1200px]
和[1200px, ∞]
。而480px
落在了[320px, 760px]
这个区间,取最大值760px
,因此img
最终选择的图像是cat-m.jpg
。
在sizes
中还可以指定媒体查询的条件,比如下面这个示例:
<img
src="cat@2x.jpg"
alt="一只灰色的猫"
sizes="(min-width: 640px) 640px, 320px"
srcset="cat@1x.jpg 320w,
cat@2x.jpg 640w,
cat@3x.jpg 960w"
/>
其中sizes="(min-width: 640px) 640px, 320px"
的意思是,如果屏幕当前的CSS像素宽度大于或等于640px
,则图像的CSS宽度为640px
,反之,则图像的CSS宽度为320px
。
而srcset="cat@1x.jpg 320w, cat@2x.jpg 640w,cat@3x.jpg 960w"
将会复杂一点。比如,如果设备的dpr="2"
时,屏幕CSS宽度为375px
,则图像CSS宽度为320px
,那么我们可以这样来计算:
› 320 / 320 = 1
› 640 / 320 = 2
› 960 / 320 = 3
计算出来的1
、2
、3
对应的就是算出来的有效dpr
,与其匹配的图像就是cat@2x.jpg
。如果dpr
变了,对应的计算方式是类似的。
如果你想了解srcset
和sizes
更多的相关知识,还可以阅读下面这些文章:
- Srcset and sizes
- 响应式图片
srcset
全新释义sizes
属性w
描述符 - Responsive images with srcset and sizes
- Top tips for web performance
- 响应式图片
- 图像
在HTML5中的<picture>
元素,也可以实现类似的功能:
有关于<picture>
更详细的介绍,可以阅读《如何使用 HTML5 的picture元素处理响应式图片》一文。
延迟加载图像:loading
<img>
元素新增的loading
属性非常适用于图像的延迟加载,该特性也被称为原生的图像延迟加载特性。在该特性还未出现之前,延迟加载图像都依赖于JavaScript库,比如lazyload
。该特性主要有三个值:
auto
:浏览器的默认延迟加载行为,和不设置loading
属性效果相同eager
:立即加载图像,不管它是否在可视视口之外lazy
:延迟加载图像,直到它和视口接近到一个计算得到的距离,由浏览器定义
如果你要延迟图像加载的话,像下面这样使用即可:
<img src="https://static.fedev.cn/sites/default/files/blogs/2020/2003/cat-632x475.jpg" alt="一只灰色的猫" width="632" height="475" loading="lazy" />
有关于图像延迟更多的讨论还可以阅读下面这些文章:
- 延迟加载图像和视频
- Lazy Loading Images
- Intersection Observer API Makes Lazy Loading a Snap
- Lazy Loading Images using the Intersection Observer API
- Lazy Loading Images with Vue.js Directives and Intersection Observer
- Native lazy-loading for the web
- Native image lazy-loading for the web!
- Lazy loading images with vanilla JavaScript
小结
<img>
看上去是HTML中一个简简单单的标签元素,但事实上他涉及到的相关知识体系还是很复杂的。特别是近年来,浏览器开发人员和HTML标准化团队成员不断的在优化和提高<img>
的能力,比如文章中提到的一些新特性,给Web中使用图像带来很多的变化和提高。
作为Web开发人员,我们除了要跟进这些新特性的使用和相关技巧之外,也应该尽可能的参与到标准化的推进工作中来。让Web的能力越来越强。最后希望这篇文章能更好的帮助你使用图像,如果你在这方面有相关的经验和更好的建议,欢迎在下面的评论中与我们共享。