> For the complete documentation index, see [llms.txt](https://lizh.gitbook.io/knowledge/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lizh.gitbook.io/knowledge/css-xi-lie/12-yi-wen-che-di-nong-dong-css-zhong-de-chang-du-dan-wei.md).

# 一文彻底弄懂CSS中的长度单位

CSS 中表示长度的单位有很多，常用的px、pt、rem、百分比等，随着移动端和响应式的流行，vh、vw也开始普遍使用。一些设置 CSS 长度的属性有 width, margin, padding, font-size, border-width等。

本文旨在整理一下 CSS 所用的长度单位，加深下对各单位的理解，比如：px 和 pt 的区别、rem 和 em 的区别、百分比在不同场合的参照对象。。。

## 屏幕的一些概念

理解css中的长度单位，我们要先知道屏幕尺寸，什么是物理像素、逻辑像素、像素、DPR、PPI，以及它们之间是什么关系?

![image-20200822005526402](https://my-files-1259410276.cos.ap-chengdu.myqcloud.com/md_images/image-20200822005526402.png)

### 屏幕（主屏）尺寸

屏幕（主屏）尺寸是指手机的物理尺寸，通常以英寸为单位。

一个手机屏幕是6.67英寸，这个6.67英寸指的是什么呢？有一段时间，我以是指手机屏幕的宽度，后来发现这是错的。屏幕尺寸指的是**屏幕对角线的长度**。

从上面图片中可以看出，屏幕的宽、高和对角线构成一个直角三角形，对角线也就是三角形的斜线，它的长度是宽高根据勾股定理计算出来的：

![image-20200822032436311](https://my-files-1259410276.cos.ap-chengdu.myqcloud.com/md_images/image-20200822032436311.png)

### 像素（pixel）

像素是是屏幕的最小单位，其物理尺寸是不固定的，主要取决于屏幕DPI/PPI。

屏幕的像素概念和图片类似，我们以图片为例，可以更直观理解：

如果一张图片不断细分，最终的最小单位是像素。也就是说，图片由许多的像素点构成，像素点可以理解为小方块，这些小方块都有一个明确的位置和被分配的色彩数值，小方块颜色和位置就决定该图像所呈现出来的样子。

下图右侧图是左侧图片放大到 3200% 时呈现的像素，是由x、y轴方向的带颜色的多个小方块组成的，每个小方块就是一个像素点。

![image-20200820013644096](https://my-files-1259410276.cos.ap-chengdu.myqcloud.com/md_images/image-20200820013644096.png)

### DP(设备像素)

DP 是英文 **device pixel** 的缩写，设备像素，也称为**物理分辨率（物理像素）、标准分辨率**，是指屏幕上纵横方向上的实际拥有的像素（点）数，单位是px。

比如：分辨率 2340 × 1080 的意思是水平方向含有像素数为1080个，垂直方向像素数2340个。

在同一类设备上，它的物理像素是固定的，这是厂商在出厂时就设置好了的，即一个设备的分辨率是固定的。

### DIP(设备独立像素)

DIP 是英文 **Device Independent Pixel** 的缩写，**设备独立像素，也称为逻辑像素、CSS像素**，可以理解为反映在 CSS/JS 程序里面的像素点。不管在何种分辨率的设备上，设备独立像素的大小应该是一致的，它是一个相对单位，相对于设备像素。一个设备独立像素的最终大小取决于文档的缩放程度和设备的 DPR。

比如：我们在 chrome 调用工具看到的设备尺寸。

![image-20200822033404072](https://my-files-1259410276.cos.ap-chengdu.myqcloud.com/md_images/image-20200822033404072.png)

### DPR(设备像素比)

DPR 是英文 **Device Pixel Ratio** 的缩写，设备像素比，是一个设备的物理像素与逻辑像素之比。

```js
DPR = DP/DIP
```

未缩放状态下，DPR 表示css中的1个像素占用多少设备像素，如2代表css中的`1px` 用 2 设备像素来绘制。

### DPI

DPI 是英文 **Dots Per Inch** 的缩写，每英寸点数，是一种解析度的指标。一般来讲，你可以把 Dot 理解为取样点，即物理设备可以解析的最小单位，常用在打印机、扫描器、滑鼠等物理设备解析度指标上。在印刷时，它就可以作为印刷网点，而在鼠标等电子设备上，可以理解为最小操作阈值（即设备会把多么远的两个点当作一个点来处理）。

我们通常讲的打印机分辨率是多少DPI，指的是"在该打印机最高分辨率模式下，每英寸所能打印的最多"理论"墨点数"。

DPI 原来是印刷上的记量单位，但随着数字输入，输出设备快速发展，大多数的人也将数字影像的解析度用DPI表示，但较为严谨的人可能注意到，印刷时计算的网点（Dot）和电脑显示器的显示像素（Pixel）并非相同，所以较专业的人士，会用 PPI(Pixel Per Inch) 表示数字影像的解析度，以区分二者。

### PPI

PPI 是英文 **Pixels Per Inch** 的缩写，意为每英寸像素，即像素点密度，也是一种解析度的指标，指得是屏幕上的像素密度。

![image-20200822030217915](https://my-files-1259410276.cos.ap-chengdu.myqcloud.com/md_images/image-20200822030217915.png)

如：`Math.sqrt(Math.pow(2340, 2) + Math.pow(1080,2))/6.67 = 386.38797472110105`

长度单位可以分为两种：相对长度单位和绝对长度单位。

## 绝对长度单位

绝对单位（px，cm， mm，in，Q，pt和pc）意味着以此为单位的长度值与其代表的物理长度相等，比如 `width: 1cm` 即与现实世界中的1cm长度相等，也意味着绝对单位在所有的媒介上的显示效果是一致的。但这是理想情况，受显示器和不同浏览器CSS实现的差异，在很多设备上绝对单位显示的并不精确。

绝对单位直接的换算公式：`1in = 2.54cm = 25.4mm = 72pt = 6pc = 96px`

|  单位 |            描述            |
| :-: | :----------------------: |
|  px | 像素，1px = 1/96英寸（DPR=96时） |
|  pt |       点，1pt=1/72英寸       |
|  pc |   十二点活字，1pc=12pt=1/6英寸   |
|  in |  英寸，1in = 96px = 2.54cm  |
|  cm |  厘米，1in = 96px = 2.54cm  |
|  mm |  毫米，1in = 96px = 25.4cm  |

### px

css 中px像素（Pixel）是逻辑像素，其特点可以归纳如下：

* dpr=1时，1px = 1物理像素。默认情况下一个 CSS 像素应该是等于一个物理像素
* dpr=2（比如iphone 6\7\8 ）时，1px = 2物理像素；dpr=3（如iPhone6Plus ）时，1px = 3物理像素
* 为了保证阅读体验一致，CSS 可以自动在不同设备之间可以调节。即一份代码可以在不同的大小的设备之间显示，并且可以保证阅读体验一致。
* 在浏览器上通过 `ctrl +/-` 可以扩大缩小屏幕，其实就是屏幕分辩率的调低/调高。`ctrl +`屏幕放大，分辨率降低。

### pt

pt 是一个标准的长度单位， 1pt＝1/72英寸，常用于印刷当中，在AI、PS等设计软件当中字体大小所用的单位就是pt，它在广告印刷业当中十分受欢迎，也非常的好用。但现在在网页当中，pt几乎不出现了。

单位换算：1pt=1/72英寸=1/6pc、72pt=96px

### pc

绝对长度单位。派卡（Pica）。相当于我国新四号铅字的尺寸。

单位换算：1pc=12pt=1/6英寸=16px

### in

绝对长度单位。英寸（Inch）。

单位换算：1in = 2.54cm = 25.4 mm = 72pt = 6pc=96px

### cm

绝对长度单位。厘米（Centimeter）。

单位换算：1in = 2.54cm = 25.4 mm = 72pt = 6pc

### mm

绝对长度单位。毫米（Millimeter）。

单位换算：1in = 2.54cm = 25.4 mm = 72pt = 6pc

## 相对长度单位

相对长度单位指定了一个长度相对于另一个长度的属性。对于不同的设备，相对长度更适用。

|      |                         |
| :--: | :---------------------: |
|  单位  |            描述           |
|  em  |       相对于其父元素的字体大小      |
|  rem | 相对于html元素的字体大小，即根元素字体大小 |
|  ex  |      依赖于英文字母小 x 的高度     |
|  ch  |         数字 0 的宽度        |
|  vw  |     视窗宽度，1vw=视窗宽度的1%    |
|  vh  |     视窗高度，1vh=视窗高度的1%    |
| vmin |       vw和vh中较小的那个       |
| vmax |       vw和vh中较大的那个       |
|   %  |       百分比，视具体场景而定       |

### em

`em`是一种相对单位，它相对于父元素的字体大小。它的值并不是固定的，会继承**父级元素**的字体大小。如当前元素的字体大小未被人为设置，则相对于浏览器的默认字体尺寸。

假如元素 A 的父元素字体大小是 16px，那么元素 A 设置`font-size: 1.2em` 相当于 `16 * 1.2 = 19.2px` ；假如元素 A 的子元素设置 `font-size: 1.2em`，那么子元素的字体大小为 `19.2 * 1.2 = 23.04px`。

### rem

`rem` (**root em**)是一种相对单位，和`em`不同点是`rem`相对于根元素`html`的字体大小。

如：浏览器默认的字体大小是16px，其他任何地方使用 `1rem` 都是相当于16px。而当根元素 html 的`font-size` 值改变后，所有使用 `rem` 单位的地方，大小都会变化。

利用这个特性，我们常常使用 `rem` 单位进行移动端的响应式布局。以 750px 基准，html 字体大小为 16px（默认值），页面中所有长度单位都用 rem，在不同屏宽的设备中，通过 JS 修改 html 的字体大小（`window.innerWidth / 750 * 16`），实现页面缩放适配。

**绝大多数浏览器默认的字体大不都是16px**，但也有极少数例外的（如：macOS Firefox，默认 15），所以一般建议设置根元素 html 的 `font-size` 值。

### ex\ch

`ch` 和 `ex` 单位都是基于当前字体的特定单位，即它们是依赖于 `font-family` 。

`ex` 单位，被定义为当前字体 `x` 字符高度。这个单位通常用于排版微调，确保精确控制。

`ch` 单位，被定义为当前字体`0`字符的宽度。所以如果你使用的是等宽字体，那么你就可以直接定义一个盒子能够容纳多少个字符。

![image-20200819231612503](https://my-files-1259410276.cos.ap-chengdu.myqcloud.com/md_images/image-20200819231612503.png)

`ch/ex` 单位，使用的频率不高。如果使用的是等宽字体，`ch`单位可以用来布局，但是中文字体和英文字体等宽肯定是不一样宽的，所以还是不要考虑用来布局了。`ex`单位的应用场景，基本上使用`em`单位都能完成，所以也是一个可用可不用的单位。

### vw\vh

`vh/vw` 单位类似于百分比单位，不同之处在于 `vh/vw` 单位的布局不依赖于父级的宽高，而是相对于视口的宽高。 `1vh`等于1%的视口高度，`1vw`等于1%的视口宽度。若视口宽度是`750px`，那么`1vw`就是`7.5px`。

`vw` 还可以和 `rem` 方案结合，这样就不需要 js 计算来设置 `html` 字体大小。

```css
html {
    font-size: 2vw;
}
p {
    width: 1rem;
}
```

目前移动端高端机型对于视口单位基本全面支持。

### vmin\vmax

`vmin` 是指 `vh` 和 `vw` 中较小的那一个的大小，当然 `vmax` 就是 `vh` 和 `vw` 中较大的那一个。

如：浏览器视口宽 `1100px`、高 `700px`，那么`1vmin = 7px; 1vmax = 11px`；如果浏览器视口宽`800px`，高`1080px`，那么`1vmin = 8px; 1vmax = 10.8px`

很有意思的是，使用这个单位的时候我们并不关心宽高，而是按照大小来区分，所以在移动端中的应用会比较多。

### rpx

**rpx 是微信小程序解决自适应屏幕尺寸的尺寸单位。微信小程序规定屏幕的宽度为750rpx。**

无论是在iPhone6上面还是其他机型上面都是 750rpx 的屏幕宽度，拿 iPhone6 来讲，屏幕宽度为375 px，把它分为 750rpx 后， 1rpx = 0.5px = 1物理像素。

## 百分比（%）

很多 css 属性的取值都可以是百分比值。虽然形式上来说，百分比值都是数字后跟`%`的形式，但在不同的使用场合下，其意义会有很多不同。因此，百分比值可以说包含了相当丰富的内容。

要理解诸如`100%`这样的百分比值，其关键点是要明白，**百分比是一定有其对应的参照值的**。也就是说，百分比值是一种相对值，任何时候要分析它的效果，**都需要正确找到它的参照**。

一个 css 属性值从定义到最终实际使用，是存在一个过程的。这其中涉及到Specified Values（指定值）、Computed Values（计算值）、 Used Values（使用值）、Actual Values（实际值）等概念，可以想见到，百分比值实际会在这个过程中，根据它的参照计算转化为一个绝对值（比如`100px`），然后再被应用。这就是百分比值的意义。

### width & height

宽和高在使用百分比值时，其参照都是元素的包含块，在大部分情况下，包含块就是**父元素的内容区**。

一般来说，`width`参照包含块的宽度，`height`参照包含块的高度。

height 和 width 还是有一个明显的区别：当父元素 width 属性为 auto 时，子元素宽度仍然可以使用百分比设定。但是对于 height，只要子元素还是在正常文件流当中的，如果父元素的 height 属性为 auto，则子元素height设定为百分比会被忽略。规范中指出：**如果包含块的高度没有显式指出（即高度由内容决定），并且该元素不是绝对定位，则计算值为auto。**

如果元素是根元素（`<html>`），它的包含块是视口（viewport）提供的初始包含块（initial containing block），初始包含块任何时候都被认为是有高度定义的，且等于视口高度。所以，`<html>`标签的高度定义百分比总是有效的，而如果你**希望在`<body>`里也用高度百分比，就一定要先为`<html>`定义明确的高度**。

### margin & padding

对于margin 和 padding，其任意方向的百分比值，**参照都是包含块的宽度**。

**这里要特别注意一点：**`margin/padding-top/bottom` ，参照的并不是包含块的高度，而是宽度。

对于`margin/padding-top/bottom` 的百分比之所以按照 width 计算，目前主要有以下两种解释：

* 相对于height计算会引起无线循环 (这个解释可能不对)

  正常流中的大多数元素都会足够高以包含其后代元素（包括外边距），如果一个元素的上下外边距是父元素的height的百分数，就可能导致一个无限循环，父元素的height会增加，以适应后代元素上下外边距的增加，而相应的，上下外边距因为父元素height的增加也会增加，如此循环。
* 基于排版的需求 之所以按照同一个width来计算，是为了要横向和纵向2个方向都创建相同的 margin/padding，如果它们的参照物不一致，那用百分比就无法得到垂直和水平一致的留白。

### border-radius

你也许见过有人用下面的代码来让一个矩形变成刚好的圆形（请体会这个“刚好”）：

```css
.circle{
    border-radius: 50%;
}
```

对此的解释是，为一个元素的 `border-radius` 定义的百分比值，参照物是这个元素自身的尺寸。也就是说，假如这个元素宽是 60px，高是 40px（border-box 的尺寸），那么 `border-radius:50%` 的结果等同于 `border-radius:30px/20px`。

注意一点：有些移动端浏览器可能不支持 border-radius 设置为百分比。另外一个设置为圆形的办法是将值设置为比元素宽度更大，如：`border-radius: 1000px`。

### background-position

background-position 的初始值就是百分比值 0% 0%。它的百分比值，取的参照是一个减法计算值，即`放置背景图的元素大小 - 背景图的大小`，可以为负值。

这个属性包括水平位置和垂直位置，它们分别参照的是宽度减法计算值和高度减法计算值。

![image-20200820010642184](https://my-files-1259410276.cos.ap-chengdu.myqcloud.com/md_images/image-20200820010642184.png)

```html
<section class="box_01">
    <div class="item_01" style="background-position: 0 0;"></div>
    <div class="item_01" style="background-position: 30% 30%;"></div>
    <div class="item_01" style="background-position: right 20% bottom 20%;"></div>
</section>
```

[查看 background-position DEMO](https://1927344728.github.io/fed-knowledge/demo/17-css3单位.html)

### font-size

参照是直接父元素的 `font-size`。

如：一个元素的直接父元素的 `font-size` 是 14px，无论这个是直接定义的，还是继承得到的，当该元素定义 `font-size:100%`，获得的效果就是 `font-size:14px`。这个百分比，类似 `em` 单位。

### line-height

参照是元素自身的`font-size`。

如：一个元素的 `font-size` 是 `12px`，那么 `line-height: 150%` 的效果是 `line-height: 18px`。

### vertical-align

参照是元素自身的 `line-height`。

如：一个元素的 `line-height` 是 `30px`，则 `vertical-align: 10%` 的效果是 `vertical-align: 3px`。

对这个属性我还想说，尽管`vertical-align`可以使用数字，百分比值，但浏览器兼容性差异较大，在跨浏览器实现时可能需要较多hack。因此，我个人倾向于使用 `middle` 等相对来说兼容性差异较小的关键字类型的值。

### 定位用的bottom、left、right、top

参照是元素的包含块。`left`和`right`是参照包含块的宽度，`bottom`和`top`是参照包含块的高度。

### transform: translate

平移变换，在水平方向和垂直方向上也可以使用百分比，**其参照是变换的边界框的尺寸（等于这个元素自己的border-box尺寸）**。

例如，一个宽度为 `150px`，高度为 `100px` 的元素，定义 `transform: translate(50%, 50%)` 的效果是 `transform:translate(75px, 50px)`。

还可以补充一点，`translate3d` 对应是还有第三个维度的，但是，经过测试，最后的第3个值不可以使用百分比（否则样式定义无效）。至于为什么不可以参照呢，大概是因为那是神秘的第三维度吧…

## fontSize值

* medium：浏览器默认字体大小，一般为16px ( h4预设值)
* xx-small：约为 medium 的 0.6 倍 ( h6预设值)
* x-small：约为 medium 的0.625 倍
* small：约为 medium 的0.8倍 ( h5预设值 )
* large：约为 medium 的1.125倍 ( h3预设值)
* x-large：约为 medium 的1.5倍 ( h2预设值)
* xx-large：约为 medium 的2倍 ( h1预设值)
* smaller：父元素字体大小的80%
* larger：父元素字体大小的120%

## 常见疑问

### DPI 和 PPI 的区别

DPI 和 PPI从定义上来看，一个指“点”，一个指“像素”。

从本质上说，它们是相同的，因为它们都是在一英寸距离内可视单位的度量，但是通常每英寸点数是指打印的材料，每英寸像素数是指使用像素的视觉显示器。

\*\*它们两个的区别，在于使用场合。\*\*比如：表示印表机的解析度就用DPI，表示屏幕的解析度就用PPI。

### px 和 pt 的区别

**基本概念**：px 就是表示 pixel，像素，是屏幕上显示数据的最基本的点；而 pt 就是 point，是印刷行业常用单位，等于1/72英寸。

**区别：**

* px 是虚拟单位，它不是自然界的长度单位。它的物理尺寸可以很小，也可以很大。如果很小，那画面就清晰，我们称它为“分辨率高”，反之，就是“分辨率低”。
* pt 全称为 point，但中文不叫“点”，确切的说法是一个专用的印刷单位“磅”，大小为1/72英寸。所以它是一个自然界标准的长度单位，也称为“绝对长度”。1pt=0.35146mm（英制）

**注意：** `1pt = 72 / inch`；而 `1px` 取决于屏幕的 DPI，`1px = DIP / inch`，即 `px * DPI = pt * 72` 所以换算公式： `px = pt * 72 / DPI` 。

比如：电脑默认 DPI 是 96，也就是说每英寸有 96 个像素点，这种情况下 `1px = 72 / 96 * pt = 3/4pt`。

但，电脑的 DPI 是可以改变的，比如：我们将电脑的DPI设置为72，那么 `1px=1pt` 了，`px` 设置的字体比 DPI 为 96 更大。

所以，一般开发者都是使用 `px` 为单位定义字体大小，这样，**无论用户怎么设置屏幕 DPI，都不会字体大小**；而使用 `pt` 为单位，当用户设置 DPI 超过 96 时，数值越大，字体就越大。

### 什么是 Retina？

Retina 是一种显示技术，可以将把更多的像素点压缩至一块屏幕里，从而达到更高的分辨率并提高屏幕显示的细腻程度，这种分辨率在正常观看距离下足以使人肉眼无法分辨其中的单独像素。

最先使用 Retina 屏幕是 `iphone 4`，屏幕分辨率为 `960 * 640（326ppi）`。

iPhone 4 分辨率比之前 iPhone 3GS 类的更高，但它不同于普通的电脑显示器那样为了显示更多的内容，而是靠提升单位面积屏幕的像素数量，即像素密度来提升分辨率，来提升显示相同内容时的画面精细程度。这种提升方式是，这样做的主要目的是为了**提高屏幕显示画面的精细程度。**

**注意：** 在桌面显示器调整显示分辨率，比如从 `800 * 600` 调整到 `1024 * 768`，屏幕的文字图标会变小，显示的内容更多了。但 Retina 显示技术不会，它是解决显示画面精细程度的问题，而不是解决显示内容容量的问题。

### em 和 rem 的区别

* em： 是一个相对单位，是相对**父级元素的字体大小**。如果字体大小未被人为设置，则相对于浏览器的默认字体大小。
* rem： 也是一个相对单位，但相对的只是**根元素 HTML的字体大小**。这个单位可谓集相对大小和绝对大小的优点于一身，通过它既可以做到只修改根元素就成比例地调整所有字体大小，又可以避免字体大小逐层复合的连锁反应。

### 如何获取屏幕DPI

```js
function getScreenDPI() {
    if ( window.screen.deviceXDPI !== undefined ) {
        return [window.screen.deviceXDPI, window.screen.deviceYDPI]
    }

    const tmpNode = document.createElement( "div" )
    tmpNode.style.cssText = `
    	width: 1in;
    	height: 1in;
    	position:absolute;
    	left:0;
    	top:0;
    	visibility:hidden;
    `
    document.body.appendChild( tmpNode )
    const arrDPI = [parseInt( tmpNode.offsetWidth ), parseInt( tmpNode.offsetHeight )]
    tmpNode.parentNode.removeChild( tmpNode )
    return arrDPI
}
```

## 参考资料

[CSS单位全解](https://yeaseonzhang.github.io/2017/11/24/CSS单位全解/)

[详述css中的百分比值](http://acgtofe.com/posts/2014/06/percentage-in-css)

[CSS长度单位：px和pt的区别](https://www.cnblogs.com/chinhr/archive/2008/01/23/1049576.html)

[十分钟快速理解DPI和PPI，不再傻傻分不清！](https://www.jianshu.com/p/aaa9fafdbc20)

[分辨率相关：屏幕尺寸、px、pt、em，物理像素和逻辑像素，以及 DPI、PPI …](https://github.com/FatliTalk/blog/issues/58)
