理解像素和其他CSS单位
本文根据 Vincent Hardy, Sylvain Galineau 的 Understanding pixels and other CSS units 翻译,无法保证译文完全正确。
摘要
本指南介绍CSS像素和其他单位之间的关系,以及CSS和设备像素之间的关系。
介绍
越来越多的CSS长度单位为web开发者提供了新的灵活性(参见CSS值和单位规范)。例如,rem(根em)单位允许在整个文档中以根元素的字体大小控制尺寸。
它们可以帮助开发人员显示内容,不依赖于显示大小和分辨率。
显示独立性:布局适配
现在的页面需要为各种各样的浏览环境做好准备:智能手机、平板电脑、大型显示器甚至电视屏幕,这覆盖了大量的尺寸、纵横比、像素密度和浏览距离。有一些工具可以帮助开发人员优化他们的布局以获得最好的体验,例如避免或减少笨拙的滚动。
媒体查询和viewport设置
现在大多数开发人员都熟悉媒体查询的使用。它们可以根据显示媒体因素(如尺寸或纵横比)来应用CSS规则。它们可以用于为每个目标环境指定单独的样式表,或者可以改进和调整主样式表。
对于移动端来说,理解和设置显示视口)尤其重要,因为它允许页面内容适配用户设备的屏幕。
百分比单位
百分比单位自CSS1引入,允许相对于包含块来设置元素的大小。例如,我们可以这样设置一个文档的body:
body {
width: 80%;
max-width: 900px;
margin-left: auto;
margin-right: auto;
}
…这样可以确保body最多900px,否则占视口宽度的80%。(请注意,CSS像素不是设备像素,这将在后面详细讨论)
其他有用的相对单位
其他几个支持自适应布局的CSS单位。下表列举了其中的一些:
| 单位 | 描述 | 示例用例 |
|---|---|---|
| em | 1 em是使用该单位的元素的字体大小的计算值。 | 例如,<h1>标题元素的字体大小可能被设为3em,body被设为1em,以确保在任何显示条件下,标题文本将是正文的3倍。必须注意的是,当用作字体大小font-size的属性值时,em单位是指父元素的字体大小。因此,在我们的示例中,在<h1>中的字体大小为2em的元素的文本是正文的6倍。 |
| ex | 1 ex是当前字体的x-height。x-height通常(但不总是,例如字体中没有x)就等于小写x的高度。 | 在实际中很少使用。可能用于调整内联图像的大小以适应当前字体的x-height,以实现视觉上的和谐。 |
| ch | 1 ch是当前字体中字符0的宽度。ch表示字符。 | 可用于等宽字体或盲文。 |
| rem | 1 rem是文档根元素的字体大小属性的计算值。这个单位通常比em单位更容易使用,因为它不像em单位那样会受继承的影响。 |
例如,根元素字体大小为20px,将<li>元素的字体大小设为0.5em,解析后,第一级<li>为10px,但第二级<li>为5px。将字体大小设置为0.5rem则不论嵌套级别如何,<li>元素字体大小都为10px。 |
| vw | 1 vw是视口宽度的1%。vw表示视口宽度。 | 可用于控制适配不同视口宽度的盒子的尺寸。 |
| vh | 1 vh是视口高度的1%。vh表示视口高度。 | 可用于控制适配不同视口高度的盒子的尺寸。例如,可以用来设置图像的最大高度,这样它就不会超过视口范围。 |
| vmin | 等于vw和vh中较小者 | 参见vh/vw |
| vmax | 等于vw和vh中较大者 | 参见vh/vw |
canvas和“全面像素控制”用例?
到目前为止,我们一直关注使用CSS设计文档元素的样式。然而,有些用例要求对每一个像素进行全面的应用控制,例如视频游戏。
Canvas 2D上下文和可缩放矢量图形都可以用于处理这些需求,WebGL也可以。在非常具体的情况下(如游戏),也可以使用绝对定位的内容来获得更快的性能。
虽然开发人员不应该随意地实现自己的布局,但是在某些用例下,相比迁移到原生应用程序开发,这仍然是一个更好的选择。
分辨率无关的渲染
但是让我们回到最基本的问题:什么是分辨率的独立性,为什么它很重要?
分辨率独立性定义
当内容被绘制到输出媒体(如打印机或屏幕)时,软件会把需要绘制的内容的描述转换成实际的像素。例如,一行文本首先被转换为一组由字体数据定义的几何轮廓;然后这些轮廓被“栅格化”,或者变成像素。同样的过程也会发生在更简单的形状上,例如在特定位置(x/y坐标)绘制的特定大小(宽度和高度)的矩形。
作为一种渲染方法,分辨率独立性要求以独立于输出媒体的确切特性的方式来描述对象。目的是能够指定需要绘制的内容,并让底层软件在运行时确定如何为特定的输出设备进行渲染。
当输出设备的尺寸和像素密度随着现代浏览设备的不同而不同时,这一点显得尤为重要。例如,在96dppi屏幕(dppi = device pixel per inch每英寸设备像素数)分辨率下,一毫米大约为4个设备像素,这样一个位置为(x=10mm,y=20mm)的矩形将被定位于x=40设备像素和y=80设备像素。当显示屏为300dppi时,一毫米大约为12个设备像素,而矩形应该位于x=120设备像素和y=240设备像素。然而,这是很重要的部分,这个矩形将出现在显示屏的相同物理位置上,在x轴上大约10mm,在y轴上大约20mm。
可缩放内容
为了分辨率独立性,系统必须能够根据渲染条件来缩放内容。Postscript和PDF是基于某种单位的概念的技术示例,这种单位可以按需缩放以适应可用的显示分辨率。两者都使用点point单位,1点被定义为1/72英寸。
可缩放矢量图形(SVG)有着相同的行为,并且有一个user unit的概念,所有其他单位最终都来自于这个单位;CSS定义了CSS像素,所有其他单位最终都解析为CSS像素(一个SVG user unit与一个CSS px相同)。
在所有这些情况下,对象的位置和尺寸最终都解析为一个单位,该单位会被映射为一些设备像素并根据需要缩放,例如当用户放大内容时。
在我们深入研究CSS的px单位之前,我们将注意到,SVG等可缩放格式是实现图像资源的分辨率独立性、响应式的非常有效的方法。
注意: 图标字体 Icon fonts 是2013年的另一种流行的做法,例如:参见http://css-tricks.com/html-for-icon-font-usage/,或者http://nimbupani.com/markup-free-icon-fonts-with-unicode-range.html。还有像Chartwell或Symbolset这样的聪明的OpenType(一种字体格式)的技巧。这些都是当今可缩放内容的插件库的一部分。
在CSS像素、物理单位和可缩放性上
虽然CSS值和单位规范在一个文档中定义了所有CSS单位,但是要理解CSS是如何将其单位与实际度量
或物理单位联系起来还是稍微费力的。规范的全部内容可以表述为:
96 px = 1 in
简单的数学指导规范中允许的两种行为:
- 在高分辨率的设备(如今的激光打印机,未来的屏幕)上 —— CSS渲染应该将一英寸映射为相应的物理尺寸(这就是规范所说的“将物理单位与它们的物理度量联系起来”)。因此,CSS的“px”单位(因为它是一英寸的1/96)可能会解析为数量为分数的设备像素。例如,在300dppi(每英寸设备像素数)屏幕上,设备像素与CSS像素像素的比值是300/96=3.125。因此,如果你这样设置元素的样式:
border: 1px solid blue;
…边框应该宽为3.125个设备像素。边框的蓝色会覆盖3个完整的像素,还会覆盖第四个像素的一部分,这部分覆盖使用抗锯齿来与背景混合 —— 这样的渲染取决于光栅器(将基本图形转换成像素的软件的一部分)。
- 在一个低分辨率的设备上,该规范建议“将像素单位与参考像素联系起来”,并进一步建议“像素单位是指最接近参考像素的设备像素的全部数量”。在前面的示例中,蓝色边框可能是一个完整的设备像素。
直到几年前,一个CSS像素通常被映射为一个屏幕像素。因此,CSS的一英寸并不总是映射为实际的物理英寸;如果一台笔记本电脑的真正分辨率是120dppi,那么96px的英寸将会是96/120=0.8的物理英寸!
随着高密度屏幕的出现,出现了一个CSS像素等于2个设备像素的设备(例如苹果的视网膜屏幕),以及像素比为分数的设备(参考这篇MDN文章)。注意,分数像素比可能会在渲染中引入额外的抗锯齿,就像高分辨率渲染那样。
简单示例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>CSS Units px/in test</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
</head>
<style>
body {
background: #404040;
}
.css-box > span {
display: inline-block;
height: 1em;
border-right: 1px solid black;
}
.css-box.px > span {
width: 96px;
background: #fefefe;
}
.css-box.in > span {
width: 1in;
background: #4166B5;
}
</style>
<body>
<div class="css-box px"></span><span></span><span></div>
<div class="css-box in"></span><span></span><span></div>
</body>
</html>

图1:在OSX Safari中的渲染
当我们尝试在不同的设备上渲染这个文档时,我们会看到:
- 在所有情况下,浅色的盒子和蓝色的盒子大小完全相同。这是因为1个CSS英寸总是和96个CSS像素的长度一样,白色的盒子是96px宽,蓝色的盒子是1英寸宽。因此,不出所料,他们的宽度相同。
- 在分辨率为110dpi的MacBook Pro 15英寸显示屏上,盒子的物理宽度为:96*1/110=0.872英寸。这是因为CSS像素与设备像素的比例是1。在我的屏幕上使用尺子测量的结果是0.88英寸,差值源于尺子精度和视觉近似:-)。所以1个CSS英寸比1个物理英寸少了22.8%。
- 在分辨率为326dpiiphone5上,一个盒子的物理宽度是96*2/326=0.589英寸。这是因为在这个平台上,CSS像素与设备像素的比例是2。再次使用尺子,我得到了0.592英寸。再一次,测量误差。这里,1个CSS英寸比1个物理英寸少了41.1%。
- 在一台打印机上(我用的是Canon Pixma MP600),盒子的物理英寸是…1.05英寸! !在这台打印机上误差为5%。
所以…一个像素不是一个像素,一英寸不是一英寸?
好吧,差不多就像这样,但并没有看上去那么糟。这里是具体原因:
- CSS像素是一个“参考”像素,而不是一个设备像素。 这是有误导性的,而且我个人更倾向于使用SVG使用的
user unit概念,因为我认为这样更容易解释对物理单位和设备像素的映射。但一旦明白“px”实际上是一个参考,而不是一个设备像素,事情就更有意义了。要记住的是,CSS px是一个抽象的单位,有一个比例控制着如何将它 a) 映射为实际的设备像素 以及 b) 映射为物理单位(以固定的方式,比率总是96个CSS px到1英寸)。 - 一个CSS英寸等于或接近一英寸。 在高分辨率的设备上,如果没有其他参数干扰(比如用户缩放或CSS变换),那么一CSS英寸将是一物理英寸。在低分辨率设备上,会出现一定幅度的错误,如上所述。
- 可缩放性和适应性才是最重要的。 对于大多数开发人员来说最重要的是,内容布局可以随着单位缩放以可预测和合理的方式回流和适应。虽然在所有设备上保持精确的纵横比的概念似乎很有吸引力,但它却在低分辨率的设备上(如不必要的反锯齿导致模糊的渲染)不受欢迎。
最终的想法
那么,作为web开发人员,为了在不同的显示尺寸、形式因素和像素密度下很好的渲染内容,我们应该牢记什么?这里有一些方法:
- 使用媒体查询,根据渲染条件(例如,小型设备屏幕,平板电脑,桌面,大型显示器)使用所需的布局。
- 在移动浏览器中使用viewport元标签控制布局。
- 使用CSS单位和CSS布局,使页面内容流畅和大小符合要求。使用rem、vh和vm(检查他们的兼容性)等最新单位,或者百分比、em或pt等有用的旧单位。
- 理解CSS像素是一个抽象的参考像素,并且要记住关键规则 —— 96个CSS像素总是与1个CSS英寸有着相同的长度。
- 在任何可能的地方(取决于图像类型和/或目标浏览器)使用SVG(或图标字体,更有限但支持更广泛),这样内容可以自然地缩放到更高像素密度或更大的尺寸。