2、视口与像素:视口(Viewport)概念、物理像素与逻辑像素、设备像素比(DPR)、CSS像素与视口单位(vw/vh)
好,咱们进入第二个话题。说实话,视口和像素这块,是很多前端新手翻车的重灾区。我自己刚入行那会儿,也在这上面栽过跟头——明明在电脑上看着好好的页面,一放到手机上就变得跟蚂蚁一样小。嗯,今天咱们就把这事彻底捋清楚。
2.1 视口(Viewport)到底是什么?
先问个问题:你打开手机浏览器,看到的那个矩形区域,它有多大?
你可能觉得,手机屏幕多大,它就有多大。但实际不是这样。浏览器里有个「虚拟窗口」,它决定了网页怎么布局、怎么缩放。这个虚拟窗口,就是视口。
我习惯把视口分成两种来理解:
- 布局视口(Layout Viewport):CSS 布局参考的「画板」宽度。早期手机为了显示 PC 网页,默认设成 980px 左右。
- 视觉视口(Visual Viewport):你眼睛实际看到的那个窗口。比如你双指缩放时,视觉视口在变,但布局视口不变。
说白了,布局视口是「画布」,视觉视口是「取景框」。两者不一致时,就会出现滚动条或者缩放效果。
关键点:移动端必须加 <meta name="viewport" content="width=device-width, initial-scale=1.0">。不加的话,布局视口默认 980px,你的 375px 设计稿放进去,字就跟芝麻一样小。
我的习惯:每次新建项目,第一件事就是检查这个 meta 标签。曾经有个同事忘了加,上线后用户反馈「字太小看不清」,排查了半天才发现是这行代码漏了。
2.2 物理像素 vs 逻辑像素
这两个概念,我当年学的时候绕了好久。咱们用大白话讲:
- 物理像素:屏幕面板上真实发光的点。iPhone 14 Pro 的屏幕有 2556×1179 个物理像素点。
- 逻辑像素:CSS 里你写的
width: 375px那个 px。它是个抽象单位,跟物理像素没有直接对应关系。
为什么会这样?因为屏幕技术发展了。早期屏幕 1 个逻辑像素 = 1 个物理像素。后来 Retina 屏出来了,同样大小的屏幕塞进了更多物理像素,显示更细腻。但如果你写 CSS 时直接按物理像素来,那 1px 的边框在 Retina 屏上会细到看不见。
所以行业约定:我们写 CSS 时用逻辑像素,浏览器自己换算成物理像素去渲染。
| 概念 | 本质 | 举例 |
|---|---|---|
| 物理像素 | 屏幕硬件的最小发光单元 | iPhone 14 Pro 是 2556×1179 |
| 逻辑像素 | CSS 里写的 px,与设备无关 | 设计稿标注的 375px |
2.3 设备像素比(DPR)—— 换算的桥梁
物理像素和逻辑像素之间怎么换算?靠的就是 DPR。
DPR = 物理像素 / 逻辑像素
举个例子:
- iPhone 6/7/8:逻辑宽度 375px,物理宽度 750px,DPR = 2
- iPhone 14 Pro Max:逻辑宽度 430px,物理宽度 1290px,DPR = 3
你写 width: 100px,在 DPR=2 的屏幕上,实际用了 200 个物理像素去渲染。这就是为什么 Retina 屏上文字更清晰——同样的 CSS 尺寸,用了更多物理点来描绘细节。
我曾经踩过的坑:做 1px 边框时,直接写 border: 1px solid #000,在 DPR=3 的屏幕上看起来太粗。后来用 transform: scale(0.333) 或者 0.5px 方案才解决。记住:1px CSS 不等于 1px 物理线宽。
2.4 CSS 像素与视口单位(vw/vh)
CSS 像素就是我们一直在说的逻辑像素。但除了 px,还有两个特别有用的单位:vw 和 vh。
- 1vw = 视口宽度的 1%
- 1vh = 视口高度的 1%
举个例子:如果视口宽度是 375px,那么 width: 50vw 就等于 187.5px。这个值是实时计算的——你旋转手机、改变窗口大小,它都会跟着变。
我个人特别喜欢用 vw 做字体大小。比如:
/* 字体随视口宽度等比缩放 */
h1 {
font-size: 4vw; /* 375px 宽度下 = 15px,750px 宽度下 = 30px */
}
/* 全屏容器 */
.full-screen {
width: 100vw;
height: 100vh;
}
避坑指南:100vh 在移动端 Safari 上有个经典问题——地址栏会占用一部分高度,导致 100vh 的内容被地址栏挡住。我曾经因为这个被测试妹子追着改 bug。解决方案是用 100dvh(动态视口高度)或者用 JS 动态计算。
2.5 实际项目中的组合用法
说了这么多理论,咱们看个真实场景。假设设计稿是 375px 宽,你要做一个自适应布局:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
/* 用 vw 做基准,配合 calc 做精细控制 */
.container {
width: 92vw; /* 留出左右边距 */
max-width: 1200px; /* 大屏限制最大宽度 */
margin: 0 auto;
padding: 2vw;
}
.card {
width: calc(50vw - 4vw); /* 两列布局,减去间距 */
margin: 1vw;
font-size: calc(14px + 0.5vw); /* 基础字号 + 动态增量 */
}
/* 处理高 DPR 屏幕的 1px 边框 */
.border-1px {
position: relative;
}
.border-1px::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 1px;
background: #ddd;
transform: scaleY(0.5); /* DPR=2 时缩放到物理 1px */
}
</style>
</head>
<body>
<div class="container">
<div class="card">卡片 1</div>
<div class="card">卡片 2</div>
</div>
</body>
</html>
这段代码里,我用了 vw 做整体布局,calc 做精细调整,还处理了 1px 边框问题。你想想看,如果不用 vw,而是写死 px,那在不同宽度的手机上,卡片间距和字体大小就会忽大忽小,用户体验很差。
2.6 总结一下
视口和像素这块,核心就三件事:
- 加 meta viewport 标签——这是移动端适配的起点,忘了它后面全白搭。
- 理解 DPR 的存在——写 CSS 时用逻辑像素,但要知道物理像素的存在,尤其是处理 1px 和图片清晰度时。
- 善用 vw/vh——它们让响应式布局变得简单,但要注意 Safari 的 100vh 陷阱。
我记得刚带团队时,有个新人问我:「为什么我写的 rem 方案在有些手机上不准?」我让他查了 DPR 和视口设置,结果发现他忘了加 meta 标签。嗯,基础的东西往往最容易忽略。希望今天的内容能帮你少走这些弯路。
公众号:蓝海资料掘金营,微信 deep3321