10分钟了解前端各种像素,em,rem等概念
2024-07-02 11:40:03

前端经典的em,rem,还有像素概念的问题,我发现很多说法都不统一,怎么说的都有,搞得我都有点不好抄统一的面试回答了。

干脆这里结合自己的理解,统一整理下,以便防止面试时候混乱。

正文

关于像素问题,几乎提到移动端适配这方面,这些基础概念就是在面试中绕不开的。

看了很多面试中都提到相关的问题,各种说法混乱无比,索性自己这里一把梭,把所有的概念都整理一下。

分辨率

我们实际开发中常说分辨率,但开发中说的内容可能指向的是不一样的内容。

物理分辨率

指的是显示屏的最高分辨率,又称标准分辨率。

屏幕实际存在的像素行数乘以列数的数学表达方式,是显示屏固有的参数,不能调节,其含义是指显示屏最高可显示的像素数

我们实际使用过程,分辨率是可以改变的,比如我们手动将1920 * 1080的屏幕换成1600 * 900的分辨率,屏幕会按照你调整的要求,重新将屏幕切割为1600 * 900的像素点阵,形成新的分辨率。

不过,此时屏幕实际的物理分辨率依然还是1920 * 1080,这是屏幕自己固有的设备像素大小,并非我们可以手动调整的。

我们平时说的时候习惯性的说的分辨率,多数情况下都是指代最高分辨率,没什么人提你当前屏幕的分辨率是多少。

像素密度

正常来说,像素越大肯定是越清楚,但是这前提是要加个限定条件,那就是屏幕实际的大小要一样。

不然,物理分辨率同样是1920 * 1080的屏幕,一个尺寸是32寸,一个屏幕是100寸,实际显示效果确实一样的话,这怎么可能呢?

这里,就要引入一个概念:像素密度,ppi(pixels per inch)

像素密度,表示沿对角线每英寸长度的像素数目(单位是dpi),越大显示的越细腻。

通过上边的例子,我们应该很快就能明白,像素密度的含义。

物理像素

物理像素(px:physical pixel)又可以称为设备像素(dp:device pixel)

我们以实际设备屏幕举例,我们说的1920 * 1080的屏幕,实际上就是在说:宽度方向有1920个像素点,长度方向有1080个像素点。

三者关系

设备尺寸 × 像素密度 = 物理分辨率(设备像素)

这里举个例子:

iphone6s 对角线长度为5.5 英寸,像素密度401 dpi,分辨率 1920 * 1080,计算可得对角线的设备像素为2205.5。

5.5 * 401 = 2205.5

这里说明白了这三者的关系,我们才好说明白接下来CSS的像素概念,即逻辑像素。

CSS像素

CSS 的像素单位,其尺寸大小是相对的,也有称其为独立像素的,这里为了避免大家混乱,下边统一称呼CSS的像素为逻辑像素。

逻辑像素

提到逻辑像素(logical pixel),也可以称为设备独立像素(dip : device independent pixel)

当然,这两个名词我们大多数开发者可能都会陌生,实际上这里就是我们平常用到的CSS样式的像素单位。

物理像素与逻辑像素的关系

很多人这时候估计平时没有遇到过相关概念,所以一时间会有些懵,难道物理像素和逻辑像素有什么不同吗?难道我写了个100 * 100 的box,在小屏幕上就不是100 * 100了?

答案是:不是的。

1
2
3
4
.box {
width:200px;
height:200px;
}

比武我们这里写了一个100 * 100的box,假设在物理分辨率为100*100的屏幕上,它以100 * 100的方式展示,1个逻辑像素,用1 * 1个物理像素来表示。

而在 400 * 400 的屏幕上,我们同样让这个BOX达到充满屏幕的效果,那就需要4 * 4个物理像素来表示。

后者因为用更多的物理像素来展示,所以看起来更清晰,更精细。

设备像素比

我上边这个例子说的有个问题,为什么在有点屏幕上要以物理像素:css逻辑像素 = 1 :1 的比例展示,有的要以 4: 1的概念展示呢?

这里要引入一个概念:设备像素比(dpr : device pixel ratio)。不过这个说法也很混乱,我看很多整理的说法,有的说缩放比,也有说缩放因子的。

实际上,这就是物理像素与逻辑像素的比例概念。

我们这里为了接下来不混淆概念,统一就叫设备像素比。

1个css的逻辑像素,用更多的物理像素表示,那当时是比例越高,越清晰喽,如下例子。

1
2
标清屏:dpr = 2 / 2 = 1
高清屏:dpr = 4 / 2 = 2

因此,设备像素比越高,我们看到的网页也就越清晰,这也是为什么有时候我们看到有些手机实际屏幕很小,但看到页面的效果却很清晰。

屏幕虽小,但是实际物理分辨率和大屏一样,因此同样的网页,在更高的设备像素比的设备上显然看着更清晰,更细腻。

我们经常在设计师手中拿到的UI稿件,肯定会看到@1X,@2X,@3X,实际上就是这个就是指设备像素比。

tips:逻辑像素相对于设备像素的放大比例,可通过window.devicePixelRatio获得,pc上可以用个个性化来设置,但二者并不完全等同。

禁用缩放

提到上边的设备像素比的概念,有人可能会有疑问,按照这么说,岂不是我把页面用手势捏一下,就能改动页面的设备像素比?

是的,用户在移动端可以随意通过手势缩放网页,为了防止用户随意控制,我们便有了如下的操作。

1
2
3
4
5
6
7
8
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">

width // 设置 viewport 的宽度,正整数/字符串 device-width
height // 设置 viewport 的高度,正整数/字符串 device-height
initial-scale // 设置设备宽度与 viewport大小之间的缩放比例,0.0-10.0之间的正数
maximum-scale // 设置最大缩放系数,0.0-10.0之间的正数
minimum-scale // 设置最小缩放系数,0.0-10.0之间的正数
user-scalable // 如果设置为 no 用户将不能缩放网页,默认为 yes,yes / no

相对单位

因为不同设备的的dpi不同,有时候PC上看上去非常正常的页面,放在手机端可能就显得不正常。

为了做各端的适配,于是前端衍生出了一种概念,相对单位。

em

上面提到,想让我们的网页在不同分辨率的设备(移动端)上正常显示,最好我们的元素长宽,外边距,内边距等都是动态的。

上面说到,我们在移动端一般这样设置:

1
<meta name="viewport" content="width=device-width;initial-scale=1" >

这个时候我们1px的大小就已知,iPhone6上为:

1
1px = 1/750(分辨率) * 2(设备像素比) = 1/ 375

既然1px的大小固定了,那么我们只能动态改变一个元素设置的px了,比如说在iPhone8上是120px;而在iPhone6上需要是100px。

这时候我们可以用js去动态计算,根据屏幕大小。

但是显然很麻烦,需要对每一个元素的长宽,内边距,外边距都需要调整,这显然是一个巨大的工程。

这时候我们就可以用到em这个单位了,em单位的名称为相对长度单位,是根据它父元素的字体大小来计算的。

一般默认情况下:16px = 1em,这里要插一嘴,有的远古面试官经常会问:为什么1em要等于16px?

因为浏览器默认的字体大小都是16px,所以没有重新设置浏览器默认字体大小的话1em就等于16px。

在页面里要用到em作为单位的话,默认的1em=16px会使得在计算他们转换的值时非常不方便,比如我要把一个块里面的内容字体大小设置成18px,但我只想用em做为单位,那么情况就会这样,1em=16px,1px=0.0625em,得18px=1.125em(0.0625em*18)

这太麻烦,所以为了简化px和em之间的换算,需要在body里定义font-size:62.5%,即将浏览器初始化为即1em=10px,这样的话,上面的font-size:18px就等于font-size:1.8em

计算公式:1/父元素font-size值*需要转换的px=em值 (PS:父元素font-size的单位没有关系,无论px还是em)

这是早期的适应方式,当所有单位都采用em时,我们只需要改变body的 font-size,那么其他子元素宽度就能动态变化了,显然方便很多。

rem

rem是CSS3新增的一个相对长度单位,它的出现是为了解决em的缺点,em可以说是相对于父级元素的字体大小,当父级元素字体大小改变时,又得重新计算。

rem出现就可以解决这样的问题,rem只相对于根目录,即HTML元素。

有了rem这个单位,我们只需要通过JS调整根元素html的font-size就能达到所有元素的动态适配了,这里附上一段常用适配代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
 /**
* ================================================
* 设置根元素font-size
* 当设备宽度为375(iPhone6)时,根元素font-size=16px;
× ================================================
*/
(function (doc, win) {
var docEl = win.document.documentElement;
var resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize';

var refreshRem = function () {
var clientWidth = win.innerWidth
|| doc.documentElement.clientWidth
|| doc.body.clientWidth;

console.log(clientWidth)
if (!clientWidth) return;
var fz;
var width = clientWidth;
fz = 16 * width / 375;
docEl.style.fontSize = fz + 'px';//这样每一份也是16px,即1rem=16px
};

if (!doc.addEventListener) return;
win.addEventListener(resizeEvt, refreshRem, false);
doc.addEventListener('DOMContentLoaded', refreshRem, false);
refreshRem();

})(document, window);

vh,vw

vw ,就是根据窗口的宽度,分成100等份,100vw就表示满宽,50vw就表示一半宽。

vw 始终是针对窗口的宽,同理,vh则为窗口的高度。

这里的窗口分成两种情况

  • 在桌面端,指的是浏览器的可视区域
  • 移动端指的就是布局视口

vwvh,比较容易混淆的一个单位是%,不过百分比宽泛的讲是相对于父元素,而vwvh相对的是指代当前窗口。

适配方案分析

无论是哪种方案,本质上都是为了解决移动端适配的问题。

这里本来打算自己整理一下各种方案的优劣,没想到有人已经整理好了,而且非常详尽:一次搞懂数据大屏适配方案 (vw vh、rem、scale)

虽然是数据大屏的方案整理,但是已经够用了。

方案 实现方式 优点 缺点
vw vh 1.按照设计稿的尺寸,将px按比例计算转为vwvh 1.可以动态计算图表的宽高,字体等,灵活性较高 2.当屏幕比例跟 ui 稿不一致时,不会出现两边留白情况 1.每个图表都需要单独做字体、间距、位移的适配,比较麻烦
scale 1.通过 scale 属性,根据屏幕大小,对图表进行整体的等比缩放 1.代码量少,适配简单 2.一次处理后不需要在各个图表中再去单独适配 1.因为是根据 ui 稿等比缩放,当大屏跟 ui 稿的比例不一样时,会出现周边留白情况 2.当缩放比例过大时候,字体会有一点点模糊,就一点点 3.当缩放比例过大时候,事件热区会偏移。
rem + vw vh 1.获得 rem 的基准值 2.动态的计算html根元素的font-size 3.图表中通过 vw vh 动态计算字体、间距、位移等 1.布局的自适应代码量少,适配简单 1.因为是根据 ui 稿等比缩放,当大屏跟 ui 稿的比例不一样时,会出现周边留白情况 2.图表需要单个做字体、间距、位移的适配

结语

这些基础概念,如果不是深入研究移动端适配的人,应该不会去深入了解,毕竟这都算是以前的老古董知识了。

实际开发中,移动端大多数中小厂商为了节省成本,应该是用Uni-app一把梭,直接用rpx完成移动端单位的适配。

甚至有些项目为了图省事的,直接用px糊弄了事,很少有人去实际操心适配的问题,毕竟多数都是为了赶工期。

这里今天整理一下,也算是梳理以前有些混乱的知识体系,把以前脑海中各种混乱的名称做了个整合,心情也算是颇为舒畅。

不过,关于移动端的适配,我这里只是浅浅聊了一下,后续有时间会逐步整理相关的知识,防止面试官去面试这些乱七八糟的考验广度的面试题。

参考

px/em/rem的区别与应用

【CSS】一文搞懂 em、px、rem、vh、vw 的区别!

px/em/rem的关系与区别_不同分辨率的设备上,1px的大小是不一样的-CSDN博客

一次搞懂数据大屏适配方案 (vw vh、rem、scale)