CSS-设备像素与逻辑像素
前端适配是个麻烦事。PC端要适配不同浏览器,移动端又要适配各种屏幕。而这一切的根源,要从「设备像素」和「逻辑像素」这对概念说起。
基础概念
设备像素(Device Pixel)是屏幕的物理像素,一块屏幕出厂时就固定的发光点数量。比如 iPhone 8 是 750 × 1334 像素,这是硬件决定的,改不了。
逻辑像素(CSS Pixel / Logical Pixel)是我们写代码时用的像素。CSS 里的 width: 375px 指的就是逻辑像素。
为什么会有两套像素系统?因为手机屏幕太小了,如果 1px CSS 对应 1 个物理像素,文字和按钮会小到看不清。所以苹果在 iPhone 4 上提出了 Retina 概念,用一个逻辑像素对应多个物理像素。
DPR(Device Pixel Ratio)
DPR 就是设备像素与逻辑像素的比值:
1 | DPR = 设备像素 / 逻辑像素 |
常见的 DPR 值:
| 设备 | 逻辑分辨率 | 物理分辨率 | DPR |
|---|---|---|---|
| iPhone SE / 6/7/8 | 375 × 667 | 750 × 1334 | 2 |
| iPhone 12 Pro | 390 × 844 | 1170 × 2532 | 3 |
| MacBook Pro Retina | 1440 × 900 | 2880 × 1800 | 2 |
| 普通 1080p 显示器 | 1920 × 1080 | 1920 × 1080 | 1 |
DPR = 1 时,一个 CSS 像素就是一个物理像素;DPR = 2 时,一个 CSS 像素由 2×2 个物理像素渲染,所以画面更细腻。
由 DPR 引发的问题
1. 1px 边框变粗
最经典的问题:写 border: 1px solid #ccc,在 DPR = 2 的屏幕上,物理上实际渲染了 2px 的宽度,看起来比设计稿粗。解决方案一般用 transform: scale() 来模拟 0.5px:
1 | /* DPR = 2 下 0.5px 等效 */ |
2. 图片模糊
一张 100×100px 的图片,在 DPR = 2 的屏幕上需要 200×200px 的物理像素来渲染,但实际只有 100×100px 的数据,就只能被拉伸,导致模糊。解决方案是用 srcset 提供不同倍率的图:
1 | <img src="photo-1x.jpg" |
或者用 CSS image-set():
1 | .avatar { |
3. 媒体查询
有时需要针对不同 DPR 做不同布局,可以用 device-pixel-ratio:
1 | /* DPR = 2 */ |
视口(Viewport)
移动端还有一个「视口」概念。在没做适配的页面中,手机浏览器会用一个「虚拟视口」来容纳整个 PC 页面,导致文字看起来很小。解决方案是加 viewport meta 标签:
1 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
width=device-width 让页面宽度等于设备宽度(逻辑像素),initial-scale=1.0 禁止默认缩放。这样 CSS 中的 100vw 就等于逻辑宽度,页面就正常了。
总结
- 设备像素是屏幕物理发光点,写代码时一般不用管它
- 逻辑像素是我们写 CSS 用的单位,通过 DPR 与物理像素关联
- DPR > 1 导致 1px 边框变粗和图片模糊,需要用 transform + srcset 来适配
- 移动端一定要加 viewport meta 标签