移动端适配方案
移动端适配的挑战
移动设备屏幕尺寸繁多,从 320px 宽的小屏手机到 430px 宽的大屏手机,还有各种折叠屏和 Pad。适配的目标是让页面在不同设备上都有良好的显示效果。
viewport 设置
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
width=device-width:页面宽度等于设备宽度initial-scale=1.0:初始缩放比例maximum-scale=1.0:最大缩放比例user-scalable=no:禁止用户缩放(注意可访问性,谨慎使用)
常见适配方案
1. 媒体查询
最基础的方式,针对不同宽度写不同样式:
/* 小屏手机 */
@media (max-width: 374px) {
.title { font-size: 16px; }
}
/* 标准手机 */
@media (min-width: 375px) and (max-width: 767px) {
.title { font-size: 18px; }
}
/* 大屏手机/小平板 */
@media (min-width: 768px) {
.title { font-size: 20px; }
}
优点:兼容性好,细粒度控制 缺点:代码量多,断点维护麻烦
2. rem 适配
rem 是相对于根元素(html)的字体大小。通过根据屏幕宽度动态设置根字体大小,实现等比缩放。
// 动态设置根字体大小
function setRootFontSize() {
const width = document.documentElement.clientWidth;
// 以 375px 设计稿为基准,1rem = 100px
const fontSize = (width / 375) * 100;
document.documentElement.style.fontSize = `${Math.min(fontSize, 150)}px`;
}
setRootFontSize();
window.addEventListener('resize', setRootFontSize);
/* 设计稿:375px,测量值 75px → 0.75rem */
.box {
width: 0.75rem; /* 实际 = 75px */
height: 0.5rem; /* 实际 = 50px */
font-size: 0.28rem; /* 实际 = 28px */
padding: 0.2rem; /* 实际 = 20px */
}
优点:等比缩放,一套代码适配所有宽度 缺点:小数像素可能产生偏差
3. vw/vh 适配
视口单位 vw(视口宽度 1%)和 vh(视口高度 1%)天然与视口尺寸关联。
/* 设计稿 375px:元素宽度 75px → 75 / 375 * 100 = 20vw */
.box {
width: 20vw; /* 相当于 75px(在 375px 宽度下) */
height: 13.33vw; /* 相当于 50px */
font-size: 7.47vw; /* 相当于 28px */
}
/* 限制最大/最小尺寸 */
.box {
width: min(20vw, 150px);
font-size: clamp(16px, 4vw, 24px);
}
优点:纯 CSS 方案,无需 JavaScript 缺点:全面使用 vw 可能导致在大屏上过大,需要配合 clamp 或 max 限制
4. 结合使用 rem + vw
/* 设置根字体大小用 vw,子元素用 rem */
html {
font-size: calc(100vw / 3.75); /* 375px 设计稿,1rem = 1vw × 100 / 3.75 */
}
/* 等价于:
375px 宽度 → 1rem = 100px
414px 宽度 → 1rem = 110.4px
320px 宽度 → 1rem = 85.33px
*/
.box {
width: 0.75rem; /* 自动适配 */
}
5. 像素比(DPR)适配
高清屏(Retina)需要处理 1px 边框问题:
/* 1px 物理像素边框 */
.border-bottom {
position: relative;
}
.border-bottom::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 1px;
background: #ddd;
}
@media (-webkit-min-device-pixel-ratio: 2) {
.border-bottom::after {
transform: scaleY(0.5);
}
}
@media (-webkit-min-device-pixel-ratio: 3) {
.border-bottom::after {
transform: scaleY(0.333);
}
}
常用 CSS 工具函数
// SCSS mixin for 1px border
@mixin border-1px($color: #ddd) {
position: relative;
&::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 1px;
background: $color;
@media (-webkit-min-device-pixel-ratio: 2) { transform: scaleY(0.5); }
@media (-webkit-min-device-pixel-ratio: 3) { transform: scaleY(0.333); }
}
}
安全区域适配
全面屏(刘海屏)适配
/* iOS 安全区域 */
.safe-area {
padding-top: constant(safe-area-inset-top);
padding-top: env(safe-area-inset-top);
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
padding-left: constant(safe-area-inset-left);
padding-left: env(safe-area-inset-left);
padding-right: constant(safe-area-inset-right);
padding-right: env(safe-area-inset-right);
}
/* 固定底部栏适配 */
.fixed-bottom {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding-bottom: calc(12px + env(safe-area-inset-bottom));
}
移动端触摸事件
// 300ms 延迟问题(已由 viewport=user-scalable=no 解决)
// 或者使用 touch-action: manipulation
// 点击穿透
// 解决方案:使用 touch 事件或 fastclick 库
// 触摸事件优化
.button {
touch-action: manipulation; /* 禁止双击缩放 */
-webkit-tap-highlight-color: transparent; /* 去除点击高亮 */
user-select: none; /* 禁止选中 */
}
总结
| 方案 | 推荐场景 | 优点 | 缺点 |
|---|---|---|---|
| 媒体查询 | 简单适配 | 精确控制 | 代码量大 |
| rem | 等比缩放 | 统一适配 | 需 JS 辅助 |
| vw/vh | 纯 CSS | 简洁自然 | 大屏需限制 |
| rem + vw | 推荐组合 | 兼顾两者 | - |
推荐策略:使用 rem + vw 做页面主体适配,配合 clamp() 限制极端尺寸,再结合媒体查询处理特殊断点。1px 边框使用伪元素 + transform scale 方案。