2、视口与布局视口:物理像素、逻辑像素、CSS像素、DPR、视口meta标签详解
做移动端适配,第一个绕不开的概念就是「视口」。我记得刚入行那会儿,拿着一个PC端写好的页面在手机上打开,文字小得像蚂蚁,整个页面缩成一团。那时候我还不懂什么视口、像素比,只知道在head里加一行<meta name="viewport">就能解决问题。
后来踩的坑多了,才明白这背后是一整套像素体系在支撑。今天咱们就把这些概念彻底捋清楚。
2.1 三个像素:物理、逻辑、CSS
先问一个问题:iPhone 14 Pro的屏幕分辨率是多少?答案是2556×1179。但你在CSS里写width: 390px,它就能铺满屏幕宽度。为什么?
因为这里存在三个不同的像素概念:
| 像素类型 | 定义 | 特点 |
|---|---|---|
| 物理像素 | 屏幕面板上真实的发光点 | 硬件决定,不可改变 |
| 逻辑像素 | 操作系统抽象出来的坐标单位 | iOS叫pt,Android叫dp |
| CSS像素 | 浏览器使用的抽象单位 | 默认与逻辑像素1:1 |
说白了,物理像素是硬件的极限,CSS像素是我们写代码时用的尺子。中间那层逻辑像素,是系统帮我们做的第一次缩放。
核心结论:你在CSS里写的 width: 375px,在iPhone 6上对应750个物理像素,在iPhone 14 Pro上对应1179个物理像素。浏览器和系统帮你完成了换算,你只管写逻辑尺寸就行。
2.2 DPR:设备像素比
DPR(Device Pixel Ratio)就是连接物理像素和CSS像素的桥梁。
DPR = 物理像素 / CSS像素
举个例子:
- iPhone 6:物理像素750,CSS像素375,DPR = 2
- iPhone 14 Pro:物理像素1179,CSS像素393,DPR = 3
- 老款24寸显示器:物理像素1920,CSS像素1920,DPR = 1
为什么会这样?因为人眼在正常观看距离下,超过300PPI就分辨不出单个像素了。手机离眼睛近,需要更高的像素密度。DPR就是用来平衡「清晰度」和「开发效率」的。
我的经验:做1px边框时,DPR=2的屏幕用0.5px,DPR=3的屏幕用0.333px。我曾经直接用1px,结果在Retina屏上边框显得很粗,后来改用transform: scaleY(0.5)才搞定。
2.3 布局视口与视觉视口
这两个概念容易混淆,我当年也花了不少时间才搞明白。
- 布局视口(Layout Viewport):CSS布局的参考坐标系。你写
width: 100%,就是占满布局视口的宽度。 - 视觉视口(Visual Viewport):用户当前看到的那部分页面。缩放时,视觉视口会变化,布局视口不变。
想象一下:你拿一个放大镜看地图。地图本身是布局视口,放大镜框住的范围是视觉视口。放大镜移动,看到的内容变了,但地图没变。
在移动端,默认的布局视口宽度通常是980px(老款Android)或1024px(iPhone)。这就是为什么没有meta标签时,页面会整体缩小——浏览器把980px的内容塞进375px的屏幕里,只能缩放。
2.4 viewport meta标签详解
这个标签是移动端适配的开关。我见过很多新手直接复制粘贴,从来不看每个参数的含义。咱们一个一个拆:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
| 参数 | 取值 | 作用 |
|---|---|---|
width |
数值 或 device-width |
设置布局视口宽度。推荐用device-width |
initial-scale |
0.1 ~ 10.0 | 初始缩放比例。1.0表示不缩放 |
minimum-scale |
0.1 ~ 10.0 | 允许的最小缩放比例 |
maximum-scale |
0.1 ~ 10.0 | 允许的最大缩放比例 |
user-scalable |
yes 或 no |
是否允许用户缩放 |
注意:iOS 10+ 开始,user-scalable=no 在某些场景下会被忽略。苹果为了无障碍体验,强制允许用户双指缩放。如果你真的想禁用缩放,可以用JS监听touch事件来阻止,但我不建议这么做——会影响用户体验。
我个人习惯的标准配置是:
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
加上viewport-fit=cover是为了适配刘海屏。这个参数告诉浏览器:把内容延伸到屏幕的安全区域之外,然后我们自己用safe-area-inset-*来做内边距控制。
2.5 知识体系总览
下面这张图把本章的核心概念串起来了:
2.6 实战中的常见问题
概念讲完了,说几个我实际项目中踩过的坑:
问题1:为什么设置了width=device-width,某些Android手机还是显示不对?
有些国产ROM会修改默认的布局视口宽度。我曾经在某个项目里,华为Mate 40 Pro默认布局视口是412px,但它的逻辑宽度是393px。解决方案是在CSS里加html { width: 100vw; overflow-x: hidden; }强制兜底。
问题2:1px边框在Retina屏上太粗怎么办?
用伪元素+transform缩放:
.border-1px {
position: relative;
}
.border-1px::after {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 1px;
background: #ccc;
transform: scaleY(0.5);
transform-origin: 0 0;
}
DPR=3的屏幕,改成scaleY(0.333)。当然,更省事的做法是用0.5px,但兼容性不如transform好。
问题3:iOS Safari双击缩放导致布局错乱。
我曾经在一个H5活动页里遇到了这个问题。用户双击图片时,页面整体放大了,布局全乱。解决方案是在JS里监听gesturestart事件并阻止默认行为。但注意:这只适用于你的页面确实不需要缩放的情况。
2.7 总结
这一章的内容,说白了就是三句话:
- 物理像素是硬件极限,CSS像素是开发尺子,DPR是它们之间的换算系数
- 布局视口管CSS布局,视觉视口管用户看到什么
- viewport meta标签是移动端适配的入口,每个参数都要理解,不要无脑复制
嗯,把这些搞清楚了,后面讲rem、vw、flexible方案的时候,你就能理解它们到底在解决什么问题了。