设计稿的1px究竟有多大?作为前端应该了解的物理像素、逻辑像素、drp、dpi...

前言

我们做 pc 网页开发或者移动端 h5 开发,绕不开的就是一个不同屏幕的适配问题。都知道有几种解决方案:rem 适配、vw 适配…但为什么需要做适配呢?按设计稿中的 10px 写,在不同设备里为什么宽度看起来不一样呢?

电脑“调整系统分辨率”和“缩放”都能调整元素在屏幕中显示的大小,他们有什么区别吗?

这些问题网上的很多文章也都有讲,但花了一整天的时间翻看了很多资料,发现有的文章存在一些错误,或者讲得比较简单,容易造成误区,看得头晕眼花😵。于是自己写一篇博客,做个记录,详细解释下这些问题。

一些概念

不同的像素

设备像素(device pixels, dp)

设备像素,也可以叫作物理像素。我们可以理解为显示屏都是由许多个小方块组成的,每个小方块都可以发出不同的光,来一起组成我们的图像。这个“小方块”指的就是物理像素。

物理像素并不是固定大小的。你可以将 1920 x 1080 个物理像素放进16英寸的显示屏里,也可以将这么多物理像素放进27英寸的显示屏里。当然,相同物理像素个数下,27英寸显示屏的单个物理像素比16英寸显示屏的单个物理像素要大得多。

物理像素的密度与大小,在显示器被制造出来的时候就已经固定了,之后是没有办法改变的。

设备独立像素(device independent pixels, dip / dips)

设备独立像素,顾名思义,就是独立于设备的像素,又叫逻辑像素。我们刚刚提到,物理像素的大小是可以变化的,不同设备都不一样,那么我们在设计或者编码的过程中,用物理像素做单位没有办法保证描述的长度是相同的。

逻辑像素就是用来解决这个问题的,它是一个与设备屏幕无关,在编程和设计领域中使用的抽象概念、虚拟像素。例如,在 CSS 中,我们通常使用 px 作为单位来设置元素的宽高和位置,我们希望不同设备的 1px 肉眼看起来长得差不多大。在大多数情况下,你可以将 CSS 像素看作是逻辑像素。但他们并不是完全一样的。很多文章将 CSS 像素简单看作是逻辑像素,其实不然。

一个逻辑像素可能由一个物理像素渲染,也可能由多个物理像素渲染。在下文我们会详细解释。

CSS 像素

就像上面说的,CSS 像素是一个在 CSS 中用来统一样式大小的虚拟像素。CSS 像素的尺寸可以粗略地视为人眼可以舒适地看到的尺寸,既不能小到不得不眯着眼睛看,也不能大到看到像素化。我们平时设计稿中的 px,代码中的 px,都是 CSS 像素。

下面的网址介绍了 CSS 像素大小的定义,可以简单参考了解一下。

Absolute Lengths: the cm, mm, Q, in, pt, pc, px units:The reference pixel is the visual angle of one pixel on a device with a device pixel density of 96dpi and a distance from the reader of an arm’s length. For a nominal arm’s length of 28 inches, the visual angle is therefore about 0.0213 degrees. For reading at arm’s length, 1px thus corresponds to about 0.26 mm (1/96 inch).
The image below illustrates the effect of viewing distance on the size of a reference pixel: a reading distance of 71 cm (28 inches) results in a reference pixel of 0.26 mm, while a reading distance of 3.5 m (12 feet) results in a reference pixel of 1.3 mm.

刚刚也提到,大多数情况下可以将 CSS 像素看作是设备独立像素,但什么时候它们不相等呢?

当设备缩放比例为 100% 的时候,1 个 CSS 像素和 1 个设备独立像素的大小是相等的。设备缩放比例就是 CSS 像素边长 / 设备独立像素边长,例如,当设备缩放比为 125% 的时候,1 个 CSS 像素边长 = 1.25 个独立像素边长。

这个具体原因,我们下文会讨论。

不同的分辨率

物理分辨率

有物理像素,那么就有物理分辨率。上文提到,“物理像素的密度与大小,在显示器被制造出来的时候就已经固定了”,这个“密度与大小”就可以用物理分辨率来表示。

物理分辨率代表了显示屏中有多少个物理像素点。比如,我们常用的 1080p 显示器,物理像素就是:1920 x 1080,表示横向一行有 1920 个物理像素点,纵向一列有 1080 个物理像素点。同样,物理分辨率也是在设备出厂时就定了。我们在购买设备时,设备信息介绍的分辨率就是物理分辨率。

逻辑分辨率

逻辑分辨率,指的就是参与渲染的逻辑像素有多少个。这个是可以在设备中调节的,以我的 Mac 为例,它默认的分辨率是1728 x 1117,如果你想要每一屏展示的内容更多,就可以将分辨率调成 2056 x 1329,这样就会有 2056 x 1329 个逻辑像素参与渲染;如果你想让每一屏展示的内容更大,那就调成 1496 x 967。(至于为什么内容会放大 / 缩小,我们也在后面细讲)

指标

设备像素比(devicePixelRatio, dpr)

刚刚我们介绍了物理像素、逻辑像素和 CSS 像素,那这几种像素之间有什么关系呢?这就是由设备像素比决定的。我们这里讨论的设备像素比是由window.devicePixelRatio得出的,它代表当前显示设备的物理像素分辨率与 CSS 像素分辨率之比。此值也可以解释为像素大小的比率:一个 CSS 像素的大小与一个物理像素的大小。简单来说,它告诉浏览器应使用多少屏幕实际像素来绘制单个 CSS 像素。(来自 MDN:Window.devicePixelRatio

dpr = 物理像素 / CSS 像素,这里的比值指的是边长,比如 dpr = 2 的时候,1 个 CSS 像素将由 2 x 2 个物理像素绘制。因为像素块一般都是正方形。话不多说,上图:

在 iphone4 问世之前,所有设备的 dpr 都是 1,1 个 CSS 像素就只由 1 个物理像素渲染。iphone4 带来了一个新的概念:Retina 屏(视网膜显示屏)。Retina 屏在有限的屏幕空间内增加更多的像素,实现了高分辨率屏幕,极大优化了用户体验。同一个物理面积,Retina 屏用 4 x 4 = 16 个像素点渲染,普通屏幕用 2 x 2 = 4 个像素点渲染,那么 Retina 屏的展示显然比普通屏细腻得多。dpr 越高,单个 CSS 像素被越多的物理像素渲染,展示就越细腻。看图感受一下:

其实我们可以将 dpr 理解为缩放比例,dpr = 1 时 1 个 CSS 像素就只由 1 个物理像素渲染,dpr = 2 时 1 个 CSS 像素由 2 x 2 个物理像素渲染,相当于放大了 1 倍。所以在 2dpr 的设备中,哪怕你在设备系统的缩放比例设置的是 100% ,2dpr 也会默认给你将页面放大到 2 倍。这里设置的“100%”是相对于 dpr = 2 的比例。

每英寸点数(Dots Per Inch, dpi / DPI)

DPI 是一个量度单位,意思是指每一英寸长度中,取样或可显示或输出点的数目。 如:打印机输出可达 300DPI 的分辨率,表示打印机可以在每一平方英寸的面积中可以输出 300 X 300 = 90000 个输出点。 打印机所设置的分辨率的 DPI 值越高,印出的图像会越精细。其实和 dpr 是一个道理。

但要注意的是,DPI 只是近似度量值,它并不是一个绝对的物理值。

来自 microsoft:什么是 DPI:DPI 只是近似度量值,因为并非所有显示硬件都可以依赖来报告准确的信息。 某些计算机监视器根本不向操作系统报告 DPI,或者用户可能已将系统配置为使用与实际硬件不同的 DPI 进行呈现(例如,更改 UI 文本元素的大小)。 应用程序可以使用 DPI 来选择绘制大小的大小,但不应依赖它作为显示大小的确切物理度量。

每英寸像素数(pixel per inch, ppi / PPI)

和 DPI 一样,PPI 也是一个度量单位,指的是每一英寸长度中,有多少个像素。更确切的说法应该是像素密度,放到显示器上说的是每英寸多少物理像素及显示器设备的点距。数值越高,代表显示屏能够以越高的密度显示图像。

屏幕横向、纵向拥有的物理像素不同,PPI 怎么计算呢?它计算的是屏幕对角线的像素数。公式如下:

咱们买设备的时候,介绍的 16 英寸、14 英寸也都是对角线长度。

在度量显示器的时候,我们可以把 DPI 和 PPI 看作是一样的。他们都代表一英寸有多少个物理像素点。

影响 dpr 的因素

费了这么大劲,终于把概念都弄明白了。我们开发过程中,需要重点关注的是 dpr 这个指标。利用window.devicePixelRatio,我们可以完成一些不同 DPI 设备的 UI 适配。

那么,到底哪些因素会影响 dpr 呢?不同设备可能有不同的 dpr,那哪些设备是 1dpr,哪些设备是 2dpr?设备出厂后,dpr还能被改变吗?

设备默认 dpr ———— 和 DPI / PPI 有关系

设备默认采用什么 dpr,是根据 DPI / PPI(*因为在度量显示器的时候 DPI 和 PPI 相同,下文均用 DPI 表示)决定的。DPI 用作显示设备的工业标准,业界人士用 DPI 的值来衡量一个屏幕是否为高清屏,然后根据得到的密度分界来获得此时对应的 dpr 值。 dpr 和 DPI 相关,一般 dpr 为 DPI / 160 的整数倍。不同屏幕的 dpr 如下所示:

项名 ldpi mdpi hdpi xhdpi
密度分界(密度值) 120 160 240 320
屏幕尺寸(分辨率) 240×320 320×480 480×800 640×960
默认缩放比例(dpr) 0.75 1.0 1.5 2.0

改变系统 / 应用的缩放比例

设备出厂后,dpr 还能被改变吗?当然!

我们上文提到,可以把 dpr 看作“缩放比例”,那么当然可以通过改变缩放比例来调整 dpr。平时在浏览器中,按住 ctrl 键缩放浏览器,其实就是改变了 dpr。我们可以通过控制台检测这个变化:

在 Windows 的系统设置中,也可以改变系统缩放比例(Mac 不行)。

对于改变缩放比例,我们看下 microsoft 官方怎么说:

了解屏幕缩放问题

默认 dpi 设置为 96,这意味着 96 像素占名义上一英寸的宽度或高度。 “一英寸”的确切度量取决于监视器的大小和物理分辨率。 例如,在 12 英寸宽、水平分辨率为 1280 像素的监视器上,96 像素的水平线将扩展大约 9/10 英寸。

更改 dpi 设置与更改屏幕分辨率不同。 通过 dpi 缩放,屏幕上的物理像素数保持不变。 但是,缩放将应用于 UI 元素的大小和位置。 对于桌面和未显式要求不缩放的应用程序,DWM 可以自动执行此缩放。

实际上,当用户将缩放比例设置为 120 dpi 时,屏幕上的垂直或水平英寸将增大 25%。 所有尺寸会相应缩放。 应用程序窗口与屏幕上边缘和左边缘的偏移量增加 25%。 如果启用了应用程序缩放,且应用程序为非 dpi 感知应用程序,则窗口的大小按与其包含的所有 UI 元素的偏移量和大小相同的比例增大。

这里提到的“一英寸”,实际上就可以理解为 CSS 像素。因为物理像素是保持不变的,原先 1 CSS 像素包含了 4 x 4 个物理像素,将缩放比例设为 25% 的时候,1 CSS像素的长度就会随之增大,能包含 5 x 5 个物理像素。

注意看!从图中可以看出来,因为缩放,一屏幕能容纳的CSS像素个数会发生改变。比如缩放前,一屏幕有 1920 x 1080 个 CSS 像素,我放大到 200% ,那么一屏幕就只有 960 x 540 个 CSS 像素了。这就是为什么我上文说 CSS 像素和逻辑像素并不是完全一样的,仅通过“缩放”,是不会改变设备的逻辑分辨率的。系统缩放比 100% 的时候,逻辑分辨率是 1920 x 1080 ,系统缩放比改成 200% , 逻辑分辨率还是 1920 x 1080 ———— 参与渲染的逻辑像素个数和大小,不管怎么缩放,都应该是不变的。这时,CSS 像素和逻辑像素的比值就等于设备缩放百分比。(真的有很多文章直接把逻辑像素和 CSS 像素划等号!这是不对的)

❌❌❌ 不能改变 dpr:更改系统分辨率

我们知道,除了缩放,更改系统分辨率也能调整展示的大小,达到和缩放一样的效果。那更改分辨率会影响 dpr 吗?答案是不会。

调整逻辑分辨率和缩放不同,并没有扩大 CSS 像素的大小,而是先将整个屏幕用调整后的逻辑分辨率渲染出来,再由操作系统通过插值等算法将渲染好的内容映射到物理像素中。这样说可能比较抽象,我们用图来表示一下:

我们可以看到,虽然更改分辨率后展示的图像还是占满整个屏幕了,但是这是由于插值算法将 60 x 60 的逻辑像素拉大到占满整个屏幕的 120 x 120 了,并不是它原先就能渲染满整个屏幕。这个过程中 CSS 像素和物理像素的比值是不会变的,一直是 1 个 CSS 像素由 2 x 2 个物理像素渲染。有些人可能会有疑问,经过插值算法后展示满屏的图像,看起来 1 个 CSS 像素并不是对应了 2 x 2 个物理像素,而是更多个,为什么 dpr 不算改变呢?因为像素比通常是指系统在渲染内容时使用的缩放因子,而不是最终显示时的物理像素映射。

而我们也可以看出,被插值算法强行拉大的图像,会比原先 120 x 120 分辨率的图像看起来糊。尤其是在非整数倍分辨率的情况下,插值算法可能会导致图像的某些细节丢失或模糊。每个设备其实都有一个最佳的逻辑分辨率,比如我的电脑,物理分辨率是 3456 × 2234,像素比为 2,电脑默认的逻辑分辨率就是 (3456 / 2) x (2234 / 2),也就是 1728 x 1117。这个时候是参与渲染的逻辑像素刚好能够占满整个屏幕,不需要使用插值算法缩放,拥有着最佳的展示效果。同样,插值算法等等是操作系统层面的算法,是消耗性能的。所以切换分辨率的时候,Mac 很贴心地告诉你:“使用缩放分辨率可能会影响性能”。