响应式图片使用指南(Part1)

发布于 大漠

图片是 Web 页面上最重要的媒体元素,它可有效的向用户传递信息,但在 Web 的近三十年发展历史中,它们的适应性一点都不强。关于图片的一切,比如图片的尺寸、格式和裁剪都被其单一的 src 属性设定像顽石一样的固定下来。特别是当高清屏幕(高分辨率屏幕)和 Web 响应式设计的到来,Web 开发者更进一步感受到了图片在 Web 上使用的限制。庆幸的是,近几年随着 Web 技术的发展,<img> 元素也得到进一步的改变,它不再局限于 src 属性引入图片,也不在局限于 widthheight 属性设置图片尺寸。Web 开发者可以使用新的 srcsetsizes 属性让图片更好的适应于高分辨率的屏幕和响应式设计。除此之外,HTML5 还提供了一个新的元素标记 <picture> 来更改显示的图片以适应不同的图片显示尺寸,而且还可以在background-image 使用 image-set() 函数为不同DPR屏幕显示不同图片。

虽然在《聊聊img元素》一文中详细介绍过 <img> 的使用,而且在该文中有介绍过 srcsetsizes 以及 HTML5 的 <picture> 的使用,但并无法很好的帮助大家在响应式设计中使用图片。为了更好的了解和掌握响应式图片在Web中的使用,将和大家一起深度探讨这方面的话题。希望对大家在使用图片的时候有所帮助。

为什么要用自适应的图片

@johnallsopp早在 2000 年 4 月在 A List Apart 的 《A Dao of Web Design》一文这样说过:

"Everything I’ve said so far could be summarized as: make pages which are adaptable.… Designing adaptable pages is designing accessible pages. And perhaps the great promise of the web, far from fulfilled as yet, is accessibility, regardless of difficulties, to information."

大致意思就是:“到目前为止,我所说的一切都可以概括为:制作自适应的网页...设计自适应的网页就是设计无障碍的网页。也许 Web 伟 大承诺还远未实现,那就是无障碍,不管什么障碍,都能获得信息。”

自从 2010 年 5 月国外著名网页设计师 @Ethan Marcotte 提出响应式网页设计(Responsive Web Design,即 RWD) 概念之后,自适应网页设计就得到了很大的改善。但在构建响应式网页时碰到了最令开发者感到头痛的问题,那就是 响应式图片

响应式图片是一种可以在不同的屏幕尺寸和分辨率的设备上都能良好工作以及其他特性的图片,它仅仅只是响应式Web设计的一部分,但它也奠定了响应式 Web 设计的良好基础。不过,在 Web 发展的近 30 年历史中,图片的适应性很弱。因为对于大多数 Web 开发者而言,可能只记得 <img>src 引入单一图片源,并且仅仅依赖 widthheight 属性来设置图片尺寸,但这对于响应式图片是远远不够的 —— 限制太多。特别是随着硬件终端种类越来越多,Web 开发者为了满足图片在大的屏幕和高分辨率的屏幕上看起来很清晰,会选择尺寸最大的,清晰度最高的图片:

但这样做对于很多使用低分辨率或小屏幕用户而言是不公平的,因为无形中给这些用户增加了更多的带宽,也让他们在访问页面时变得更慢(因为高清晰,大尺寸图片文件就更大,下载就更慢)。面对这种复杂的场景时,最为理想的是:

当访问网站时依靠不同的设备来提供不同的分辨率图片和不同尺寸的图片

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

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

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

但是,响应式图片不仅仅只是尺寸和格式这么的简单。响应式图片的显示其背后有很复杂的逻辑。它涉及到确定图片的大小、以及了解用户是否在高分辨显示器上,还有其他一些事情。值得庆幸的是,浏览器比我们更有能力来处理这个逻辑。我们所要做的仅是给浏览器一个小小的提示。

几个重要概念

在深入聊响式图片技术之前,先花一点时间来了解一下几个重要的概念。这有助于我们后面的学习。

响应式 Web 设计

响应式 Web 设计即是 RWD(Responsive Web Design)。理论上,响应式 Web 页面能够适应不同的终端设备。描述响应式 Web 设计最经典的一句话是:“Content is like water”,即 “如果将屏幕看作容器器皿,那么内容就像水一样”,可以随着器皿的不同成不同的形状:

易与 RWD 搞混淆的是 自适应设计,即 Adaptive Web Design,简称 AWD。 RWD 和 AWD 都是为了适配各种不同终端设备,致力于提升用户体验所产生的设计方案(或适配方案)。核心思想是用技术来使 Web 页面适应从小到大的不同分辨率的屏幕。从历史的发展角度来说,AWD 早于 RWD,也正因此,常有人把 RWD 当作 AWD 的一个子集,甚至有很多人认为 RWD 就是 AWD。

大家都认为 RWD 的概念是源于 @Ethan Marcotte的《Responsive Web Design》一文。他提出了采用 CSS 的媒体查询特性配合流体布局技术在尽可能不改变 HTML 的 DOM 结构的基础上只改变 UI 效果和布局风格。@zeldman 曾对 RWD 做过这样的定义:

RWD是一切能用来为各种分辨率和设备性能优化视觉体验的技术

@Aaron Gustafson有一本书叫《Adaptive Design》:

他认为 AWD 在包括 RWD 的 CSS 媒体查询技术以外,还需要借助 JavaScript 脚本来操作 HTML来更适应移动终端的能力。AWD 有可能会针对移动端用户“减去内容,减少功能”。 AWD 可以在服务端就进行优化,把优化过的内容送到客户端上。

如果用一句话来描述 RWD 和 AWD 的区别的话:

RWD是一套代码,适用于所有终端屏幕;AWD则是多端多套代码!

但两者本质都是:致力于适配不同终端设备,更好提升用户体验

CSS 像素

CSS 像素(CSS Pixel,简称 px)也称为 设备独立像素逻辑像素设备无关像素,在 iOS 中称为 PT,Android中称之为 DIPDP,默认情况下在 PC 端等于一个 物理像素。比如,使用 CSS 的 px 单位给 img 元素指定了一个 375px 宽,812px的高,则刚好填满了 iPhone 11 Pro (或 iPhone X)的整个屏幕:

img {
    width: 375px;
    height: 812px;
}

如果你在设计软件中(比如Sketch)把一张 375px x 815px 放大,可以看到一个个方块点:

这些个小方块点具有特定的位置和颜色。正如上图所示,图片(或电子屏幕,比如手机,显示器屏幕等)就是由无数个具有特定颜色和特定位置的小方块拼接而成。

一般情况下, CSS 像素(px)相对的是 设备像素(Device Pixel)。CSS 像素又上个具有两个方面的相对性:

  • 在同一个设备上,每一个 CSS 像素所代表的设备像素是可以变化的(比如调整屏幕的分辨率)
  • 在不同的设备之间,每一个 CSS 像素所代表的设备像素是可以变化的(比如两个不同型号的手机)

你可能在用浏览器访问一个 Web 页面的时候,会有放大缩小的操作,会引起 CSS 中 px 的变化,可能会出大于或小于的三种情况:

另外,CSS像素也会受到 “每英寸像素(PPI)” 和 “设备像素比(DPR)” 的影响。

设备像素

设备像素(Device Pixels,简写 DP),又称 物理像素,是设备能控制显示的最小单位,我们可以把它看作显示器上的一个点,即屏幕上可以显示的最小颗粒,在同一设备上,它的物理像素是固定的。我们常说的 1920 x 1080 像素分辨率就是用的 设备像素单位。

我们平时所说的一倍屏、两倍屏和三倍屏指的是设备以多少物理像素来显示一个 CSS 像素,越多的物理像素去显示一个 CSS 像素就会提高其清晰度。

在开发的过程中,可以使用下面的 API 来获取屏幕真实的物理像素:

window.screen.width //获得屏幕水平方向上的像素数
window.screen.height //获得屏幕垂直方向上的像素数

了解了设备像素概念之后,我们来试着回答:平时设计稿中的 750px 中的像素跟 Web 布局中的 CSS 像素是一致的吗?如果你理解了 CSS 像素和物理像素概念,我想你肯定知道答案。如果你的回答是否(不一样),那说明你理解对了。是的,实际上我们在设计稿(设计师提供的稿子)的像素指的就是 设备像素,它是按照设备像素来的。

分辨率

分辨率分为 显示分辨率图像分辨率 两种。

显示分辨率

显示分辨率指的是屏幕图像的精密度,是指显示器所能显示的像素有多少。由于屏幕上的点、线和面都是由像素组成的,显示器可以显示的像素越多,画面就越精细,同样的屏幕区域内能显示的信息也就越多。

通常用每行像素数目乘以每列像素数目来表示分辨率。如 MacBook Pro 16″的原始分辨率为3072px x 1920px。就是说屏幕水平方向上有 3072个像素点;在垂直方向有1920个像素点。屏幕分辨率是机器生产时已经确定好的了,即已经规定好了机器屏幕上是有多少个像素点组合而成。但我们也知道,我们可以修改电脑的屏幕分辨率。

图片分辨率

图片分辨率则是单位英寸中所包含的像素点数,其定义更趋近于分辨率本身的定义。从这个定义上来看很明显,跟 PPI(像素密度)的含义是一样的,所以 PPI 是用来表示图片分辨率的单位,比如,100ppi的图片分辨率,其意思是每英寸中有 100 个像素。

跟显示分辨率一样,图片分辨率也可以用水平像素数 x 垂直像素数来表达。其实我们知道 ppi,也知道图片的宽高(如英寸为单位),就能算出图片是由多少个像素组成,即水平像素数 x 垂直像素数

我们要知道,图片的显示宽高尺寸,不会受到PPI值直接影响,对于计算机的显示系统来说,PPI是没意义的,真正起作用的是图片的水平像素数 × 垂直像素数,只要水平像素数 × 垂直像素数一样,就算PPI不一样,图片同样显示一样的宽高尺寸。

此外,其实在不同的应用领域,图片分辨率也可以用不同的单位进行描述,如在打印领域中,也可以用dpi来描述。

分辨率比例

分辨率比例(Resolution Ratio)同样分为屏幕分辨率比例和图像分辨率比例:

  • 屏幕分辨率比例是指屏幕的宽和高的像素比例
  • 图片分辨率比例是指图片的宽和高的像素比例

屏幕尺寸

屏幕尺寸指的不是屏幕的宽高,它说的就是屏幕的尺寸。实际上,在介绍产品时常说的,手机屏幕尺寸是 5.3 英寸,电脑屏幕多少英寸之类的,不是指屏幕的宽高,而是指屏幕的对角线长度:

设备独立像素

设备独立像素(Density Independent Pixel),简称 DIP,也称密度无关像素,又有人称 CSS 像素或逻辑像素。设备独立像素可以认为是计算机坐标系统中的一个点,这个点代表一个可以由程序使用的虚拟像素。

在 CSS 规范中,长度单位可以分为绝对单位和相对单位。px 是一个 相对单位,相对的是设备像素。比如有些iPhone设备(iPhone 12系列)使用的是视网膜屏幕(Retina),用 3 x 3 的设备像素(Device Pixels)代表 1 x 1 的 CSS 像素(CSS Pixel)。拿 iPhone 12 Pro Max来说,它的设备像素是 1284px x 2778px,而 CSS 逻辑像素是 428px x 926px

设备像素比(DPR)

设备像素比一般是指 DPRDPPX

  • DPR 是 Device Pixel Ratio 简写,指的是每个 CSS 像素的设备像素
  • DPPX 是 Dots Per Pixel 简写,指的是每 CSS 像素的设备像素数量

我们平时所说的设备像素比,更多指的是 DPR:

设备像素比,级联样式表(CSS)使用的物理像素和逻辑像素之间的比率:它的其他名称是“CSS像素比”和"DPPX"

所以,设备像素比表示 一个CSS像素(宽度)等于几个物理像素(宽度)。我们可以用下面的公式来计算设备像素比:

DPR = 物理像素数 / 逻辑像素数 = 屏幕物理像素数 / 设备独立像素数

注,上面公式中物理像素数和逻辑像素数指的是同一方向的,比如水平方向(x轴)或垂直方向(y轴)。

简单地说,一个逻辑像素(1pt)既可以对应一个物理像素点(1px),也可以对应 1.5px2px或更多个px。这也是我们常说的几倍屏:

也就是说:

DPR = 1,   1pt = 1px
DPR = 1.5, 1pt = 1.5px
DPR = 2,   1pt = 2px
DPR = 3,   1pt = 3px

对于 Web 开发者而言,我们可以使用 window.devicePixelRatio 来获取设备的 dpr:

在 CSS 中可以通过 @mediamin-device-pixel-ratio 来区分 dpr

@media (-webkit-min-device-pixel-ratio:2),(min-device-pixel-ratio:2){
    // CSS
}

当然,上面的规则也有例外,比如 iPhone6、iPhone7 和 iPhone8 Plus的实际物理像素是 1080 x 1920,在开发者工具中可以看到,它的设备独立像素是 414 px 736,设备像素比(dpr)是 3,设备独立像素和设备像素比的乘积并不等于 1080 x 1920,而是 1242 x 2208

实际上,设备会自动把 1242 x 2208个像素点塞进 1080 x 1920 个物理像素点来渲染,我们不用关心这个过程,而 1242 x 2208 被称为屏幕的“设计像素”(我们开发也是以这个设计像素为准)也称为渲染像素:

像素密度(PPI)

像素密度指的是每英寸长度上有多少个像素,又叫像素数目。像素越多,代表画面越细腻越清晰。我们常说的视网膜屏幕(Retina),就是指 PPI 较普通屏幕要高。PPI(Pixels Per Inch)是图片像素分辨率的单位,图片 PPI 值越高,画面的细节就越丰富,因为单位面积的像素数量更多。

对于一个设备来说,屏幕尺寸(指的是对角线长度),屏幕的分辨率(宽高的像素值),那么宽高和对象线就形成了一个垂直三角形。利用勾股定理,可以算出对角线的像素值。而又知道了对角线的英寸值,那么就可以算出屏幕的 PPI 值。

比如 iPhone XS Max,根据上面公式可以算出其 PPI = 458:

DPI

DPI 是指 Dots Per Inch,即每英寸点数,是一个用于点阵数码影像,指每一英寸长度中,取样、可显示或输出点的数目。这里说的点,类似打印机的“墨点”,打印成像是由这些墨点成线,线成面这样组合而成。

它是一个输出分辨率(打印分辨率),常用于描述打印机的打印精度,一般来说, DPI 值越高,表明打印机的打印精度越高。它表示每英寸所能打印的点数,即打印精度。

一般的激光打印机的输出分辨率是 300dpi ~ 600dpi,印刷的照排机达到1200dpi~2400dpi,常见的冲印一般在 150dpi ~ 300dpi之间。

图片的像素、打印分辨率和打印尺寸的关系如下:

图片的横向(或竖向)像素数 = 打印横向(或竖向)分辨率 x 打印的横向(或竖向)尺寸

例如:希望打印照片的尺寸是 4 x 3inch,而打印分辨率横向和竖向都是 300dpi,则需要照相机采集的像素数至少为 (300 x 4)x(300 x 3)=1080000像素,约一百万像素。采集的像素数过低(采集图像机器的PPI决定)会降低图像的打印质量,过高也不能提升打印质量。

PPI和DPI经常都会出现混用现象。但是他们所用的领域也存在区别。从技术角度说,“像素”只存在于电脑显示领域,而“点”只出现于打印或印刷领域。

DPR 和 PPI 相关,一般是:DPR = PPI / 160

视窗

视窗又常称视口,即 Viewport,代表当前可见的计算机图形区域。在 Web 浏览器术语中,通常与浏览器窗口相同,但不包括浏览器的 UI,菜单栏等。简单地说,指的是你正在浏览的文档的那一部分。

视窗的大小一般以 CSS 像素为单位,可以通过下面的 API 来获取:

document.documentElement.clientWidth
document.documentElement.clientHeight

另外,视窗也分为多种,主要分为: 布局视窗视觉视窗理想视窗,它们在屏幕适配中起着非常重要的作用。

布局视窗

布局视窗( Layout Viewport)指的是我们可以进行网页布局区域的大小,以CSS像素做计量单位。移动设备默认会设置一个较大的viewport(如IOS一般默认是980px),Layout Viewport 的宽度是大于浏览器可视区域的宽度的。

Layout Viewport的尺寸可以通过下面 API来获取:

document.documentElement.clientWidth
document.documentElement.clientHeight

视觉视窗

视窗视窗(Visual Viewport)即设备的像素分辨率。Visual Viewport的尺寸可以通过下面 API 来获取:

window.innerWidth
window.innerHeight

理想视窗

理想视窗(Ideal Viewport)是一个能完美适配移动设备的Viewport。无论是在何种密度屏幕,何种分辨率下,显示出来的大小都差不多。Ideal Viewport并没有一个固定的尺寸,不同的设备有不同的Ideal Viewport。理想视口与设备的宽度一致,例如iPhone8的理想视口是375px

Ideal Viewport 的意义在于,无论在何种分辨率的屏幕下,针对Ideal Viewport 而设计的网站,不需要缩放和横向滚动条都可以完美地呈现给用户。理想视窗的尺寸通过下面 API 可获取:

window.screen.width
window.screen.Height

屏幕方向

移动手持设备的屏幕方向是可以旋转的,以横屏(Landscape)和竖屏(Portrait)之分:

可以使用 window.orientation 来判断设备是横屏还是竖屏:

window.addEventListener("resize", ()=>{
    if (window.orientation === 180 || window.orientation === 0) {
        // 正常方向或屏幕旋转180度
        console.log('竖屏');
    };

    if (window.orientation === 90 || window.orientation === -90) {
        // 屏幕顺时钟旋转90度或屏幕逆时针旋转90度
        console.log('横屏');
    }
});

也可以使用 CSS 的媒体查询来区分屏幕的方向:

@media screen and (orientation: portrait) {
    /*竖屏...*/
}

@media screen and (orientation: landscape) {
    /*横屏...*/
}

媒体查询

媒体查询是指 CSS 的 @media 规则,可以根据指定的媒体类型、媒体逻辑运算符和媒体特性给元素指定样式规则:

比如使用 min-widthmax-width 来指定不同断点的条件:

借助媒体查询的特性,可以实现不同的布局效果(Responsive),也可以在不同的环境下使用不同的图片:

有关于这方面详细的介绍可以阅读:《图解CSS: CSS媒体查询》。

宽高比

宽高比指的是元素宽度和高度的比率,即 x:y。在 CSS 中可以使用 aspect-ratio来指定元素的宽高比,不过我们这里主要说的是图片的宽高比。

有关于 aspect-ratio 更详细的介绍可以阅读《使用CSS的aspect-ratio实现宽高比缩放》一文。

有了这些基础概念,我们就可以继续往下了。开始进入 srcset 的世界!