关于物理像素、逻辑像素(css像素)、分辨率、像素比的超详细讲解

4,210 阅读14分钟

前言

为什么width为1200px的同一个元素,在电脑上和pad上的显示宽度会不同?

为什么修改浏览器的“缩放”比例,可以控制页面元素的大小?

为什么同样的页面,在mac上显示起来会比很多设备更为细腻?

为了找到这些问题的答案,我仔细的去了解相关知识,看了相关的文章,可总觉得大家的文章都写的很简单,对我这种前端小垃圾来说,每次看完一篇文章,好多地方自己还是理解的不好或者根本看不懂,然后就又要去看更多的文章来辅助自己理解,相同主题的文章一篇又一篇的看,整个过程搞的我头痛,当时就想,要是有一篇给小白看的,讲的更清楚一点的文章就好了。于是这次我决定写一篇超详细讲解,来让和我一样的前端小白能节省一些时间和精力(以及不要那么头痛),如果这篇文章有帮助到你,就顺手帮我点个赞吧(星星眼)。

言归正传,要解答以上问题,我们只需要弄清物理像素、逻辑像素、像素比、分辨率到底都是什么。

物理像素

我们可以这么理解,屏幕是由N多个小方块构成的,每个小方块都只能显示一个颜色,而一个小方块,就是一个物理像素。而在不同设备上,这个小格子的大小是不同的,所以物理像素并不是一个固定大小的单位,他只代表一个显示器上的小格子,格子到底多大,在显示器出厂前已经确定好了。

逻辑像素(px)

我们可以这么理解,物理像素是硬件开发相关的单位,是基于一个一个的硬件小方块,其大小由硬件厂家设定。而逻辑像素,则更像是软件开发相关的单位,软件相关的工作人员通过逻辑像素来表达软件相关的尺寸。前端css代码、浏览器显示给开发者的尺寸、UI设计师给出的UI稿,其尺寸单位都是逻辑像素。

比如前端工程师的代码如下,其中标注的14px、26px等值,px就是我们所说的逻辑像素。

image.png

比如UI设计师提供的UI稿,其中标注的1200px,40px等也都是逻辑像素

image.png

再比如我们通过js获取的数值

image.png

以及浏览器显式展示的尺寸

image.png

这些通通都是逻辑像素

像素比(DPR)

像素比 = 物理像素/逻辑像素

注意:这里的物理像素和逻辑像素,指的是像素的个数,即绘制相同区域所需的物理像素个数/绘制相同区域所需的逻辑像素个数

由公式可知,像素比就是物理像素与逻辑像素的比值,即当前设备将通过几个物理像素点来绘制一个逻辑像素点

举个例子:

前端工程师现在要画一个2px * 2px的正方形

设备1: 像素比为1,通过2*2个物理像素点来画的这个正方形

设备2: 像素比为2,通过2 * (2 * 2),一共8个物理像素点来画的这个正方形。

注意,设备2所绘制出的正方形其物理尺寸并不一定比设备1的大,因为我们前面提到过,物理像素并不是一个固定大小的单位,其大小由厂家指定。那么如果设备2和设备1所绘制出来的正方形物理面积相同,且设备2用了更多的物理像素点来绘制,很显然,设备2的显示将更为细腻。

通过js如何获取当前的像素比

window.devicePixelRatio

不同设备的像素比可能不同

我正在使用mac+外接显示器在写这篇文章,正好拿来举个例子:

首先在mac所在窗口打开一个标签页,其像素比为2

将标签页拖动至外接显示器窗口后,像素比变成了1

相同设备上,用户可以手动修改像素比

比如:当我们缩放浏览器时,调整的其实就是像素比

image.png

分辨率

像素点总数即为分辨率,通过以上介绍,我们知道,像素有逻辑像素、物理像素之分,分辨率也一样,有逻辑分辨率和物理分辨率之分。

物理分辨率

物理像素点的总数即为物理分辨率。举两个例子:

  1. 下图为ipad pro的产品介绍文案,该段文案就说明,该款ipad每一横行有2732个物理像素点,每一纵行有2048个像素点,屏幕总共由2732*2048个物理像素点组成(也就是文中所说的560万像素)。

  2. 下图为我的windows笔记本和一台外接显示器的物理分辨率,即我的windows本和外接显示器,有着相同的物理像素(格子)数量:1920 * 1080

    image.png

逻辑分辨率

逻辑像素点的总数即为逻辑分辨率。同样举个例子:

  1. 我用iphone照了一张照片,它的属性如下:

    image.png

    照片由3072 * 4096个逻辑像素点组成,如果像素比为1,该张照片需要由显示器的3072*4096个方块来显示。而当前我们显示器的物理分辨率为 1920 * 1080。很显然,显示器的格子不够用了,所以只能显示照片的一部分。

    image.png

    而当我们把照片缩小显示后(其实就是调整了像素比),图片的逻辑像素和显示器的物理像素(格子)不再1:1显示,而是n:1显示,一个物理像素(格子)负责多个逻辑像素的绘制,这样显示器的格子就够用了

    image.png

一个常见的误区

通过上面的介绍,你理解了吗? 无论是物理像素还是逻辑像素,都与我们所理解的1cm、1mm这种恒定不变的长度单位不同

1200px(逻辑像素),在电脑上和在pad上所展示的宽度(如果用cm来衡量)是不同的,宽度多少与当前的设备甚至是当前用户的设置有关。

物理分辨率为3 * 3的设备,也不一定其物理面积就比4 * 4的设备小。

不要把物理像素、逻辑像素与生活中的单位混为一谈,觉得他们总会有一个恒定不变的尺寸值,那样你会陷入一个思维怪圈一直走不出来。

相关案例

为了加深理解,我拿出两个开发中的相关案例,来帮助大家更好的理解物理像素和逻辑像素,以及他们在实际开发中的应用。

案例一 如何根据UI稿实现自适应移动端页面

先声明,此处只是提供案例来帮助大家理解像素,并非推荐大家使用下面的方案来实现自适应,关于是否使用rem来实现自适应,争论还是很大的。

第一步 确定缩放比例B/A

通常来说,移动端我们都是基于宽度做适配的,从而保证用户所浏览的页面横向铺满不留白

要实现该需求,我们要拿到两个数据

  1. UI稿最外层元素的宽度(Apx)
  2. 用户所在浏览器的宽度(Bpx)

比如现在UI给出了这张UI稿

image.png

可以看到,UI稿最外层元素的宽度为750px,即A = 750

而用户当前所在浏览器的根元素(以iphone12 pro举例)尺寸如下:

image.png

B = 390

则我们只需要把UI稿按照B/A等比例缩放到浏览器上,即可实现页面的移动端自适应展示了。也就是说在iphone12 pro的浏览器中,我们需要把UI稿上所有UI元素的尺寸,都设定为原始大小的390/750。

第二步 通过rem实现UI元素按比例缩放

上面说,我们需要把UI稿上所有UI元素的尺寸,都设定为原始大小的390/750,可说起来容易,总不能每个元素的尺寸我们都计算一遍之后再写在css里吧?为了实现该功能,我们使用rem这个相对单位。

以下为w3c给出的rem定义:

相对于根元素(即html元素)font-size计算值的倍数

即如果我们设置了document.documentElement.style.fontSize = '14px',则此时1rem为14px,2rem为28px.....

因此想要实现将所有UI元素缩小为原始大小的390/750,我们只需要做两件事

  1. 设置document.documentElement.style.fontSize = 390/750 + 'px'
  2. css中的单位由px改为rem

下图就是我们移动端项目的真实代码,由此,自适应的移动端页面就可以实现了

image.png

image.png

关于逻辑像素与逻辑像素之间按比例缩放的进一步理解

这个案例里,无论是UI稿的750px,还是浏览器根元素的390px,都是逻辑像素,那么有的读者可能难以理解,明明单位都相同 ,为什么还需要缩放元素呢。

因为对于UI设计师来说px只是用来向我们说明他想法的一个工具

假设在750px * 1000px的UI稿上,有一个20px * 10px的按钮,和一条750 * 1px的线,此时UI设计师要向我们说明的想法是:“你看,你实现的页面要长这个样子,按钮的宽度占整张图宽度的2/75,按钮的长度占整张图长度的1/100,这条线要占据整个页面的宽度,且他的宽度要是最细最细的宽度”

想法说明白了目的也就达到了,那前端工程师这么聪明(傲娇脸),一套UI稿足够了,所以UI设计师只绘制了1套(当浏览器根元素宽度为750px时的)UI稿

对于前端开发者来说,px是用来和浏览器沟通的工具,浏览器可没有我们这么聪明,如果把每一个不同尺寸的浏览器都当成一个小朋友,那么每一个浏览器小朋友都需要我们手把手的去教他要怎么绘制我们需要的图,不要妄想像UI设计师那样简单的开个班会,就能让每个小朋友学会绘图。所以在面对每一个小朋友,我们首先要问小朋友的画板有多大(获取当前浏览器根元素宽度),然后再手把手告诉他们,每个元素的尺寸是多少(通过rem将UI稿按比例缩放)

以上,就是关于该案例的介绍。

一个像素主题外的说明

由于本篇文章意在讲解像素相关问题(且篇幅有点长 [●´Å`●]  ),为了减少大家的阅读干扰,起初我并没有对像素主题外的内容进行说明。不过有读者恰恰对我没有说明的地方产生了疑惑,进而影响了对该篇文章的理解,所以在这里做一个额外说明,如果您对当前案例的讲解没有疑惑,可以直接跳过这部分,等看完全篇文章再来看这部分也不迟,免得打断对像素的理解思路

我在前面说要设置document.documentElement.style.fontSize = 390/750 + 'px',那么我贴出来的的代码应该是下面这样

image.png

image.png

可我贴出的代码却是下面这样(与上面代码的不同之处我用红框标了出来)

image.png

也就是说,我把浏览器根节点字体的大小放大了100倍,这是不是多此一举呢?

当当当当!有一个知识点来了:不同浏览器对字体大小是是有限制的

拿chrome举例:chrome规定中文网页的字体不能小于12px,英文网页的字体不能小于6px。浏览器说:“你的页面在我的地盘上就得听我的,你设置的font-size我得先审核一下。什么?小于12px了? 那可不行,我的用户会看不清的,多影响我口碑,我给你改成12px了哈,你下次注意点,设那么小干嘛,谁看得清啊。”

所以当我们设置根节点的font-size过小时,很有可能页面显示的效果会和我们的预期不同。比如,我设置了根节点的font-size为1px, 我以为1rem此时为1px,可事实上,此时的1rem为12px,因为浏览器已经在我不知道的情况下,将根节点的font-size设置成了12px。

为了避免以上问题的发生,我们在实际开发中,会将根节点字体放大n倍,以避免其小于浏览器能支持的最小值。

案例二 不同设备上的1px显示问题

在UIDebug过程中,UI同事有时会这么问:哎?你看这个边框,有点粗啊,如果没有经验的前端er可能会拍胸脯说,不会有问题的,我css里写的就是1px。但看了上面的介绍,我们会知道,1px的逻辑像素点,在很多设备上会以2个物理像素点、甚至3个物理像素点来显示。而UI同事需要的1px边框,是细细的一条线,换句话说,这条线,UI同事希望是浏览器所能实现的最细的一条线——即宽度为1个物理像素点的线。

所以问题来了,我们的css是以逻辑像素为单位,那要如何保证我们画出的线无论在像素比为多少的设备上,都以1px物理像素来显示呢?有一种常用的方案,就是通过媒体查询来实现,对不同像素比的设备通过scale来进行缩放,比如我现在不是画了一条1px的线,并且知道这条线在像素比为2的页面上宽度为两个物理像素吗? 那我就通过scale(0.5),来把它的宽度缩回1个物理像素。注意,如果是横向边框,那我们就用scaleY,如果是纵向边框,那我们就用scaleX,这样压缩的就只是宽度,而不会压缩长度。废话不多说了,贴代码:

/* 底部1px边框 */
.border-b-1px::after {
  content: ' ';
  display: block;
  position: absolute;
  width: 100%;
  left: 0;
  bottom: 0;
  border-bottom: 1px solid #e6e6e6;
  color: #e6e6e6;
}

@media (-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5) { 
  .border-b-1px::after {
    transform: scaleY(0.7);
  }
}

@media (-webkit-min-device-pixel-ratio: 2), (min-device-pixel-ratio: 2) { 
  .border-b-1px::after {
    transform: scaleY(0.5);
  }
}

@media (-webkit-min-device-pixel-ratio: 3), (min-device-pixel-ratio: 3) { 
  .border-b-1px::after {
    transform: scaleY(0.34);
  }
}

这样修改边框代码后,UI同事和你都可以放心下班了。

最后的嘚吧嘚

好了,以上就是我要介绍的所有内容,文章的重点就是介绍物理像素、逻辑像素、分辨率、像素比这些概念,为了帮助大家理解以上概念,我在最后又给出了两个案例,不知道这篇文章对大家来说有没有帮助(我已经尽力了o(╥﹏╥)o)。如果哪些地方我理解的或者说的不对,欢迎大家指正和讨论,不过说的温柔点,太凶了我会哭的我跟你们说