响应式与移动端适配
一、响应式设计基础
1.1 视口设置
html
<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.2 媒体查询
css
/* 基础语法 */
@media screen and (max-width: 768px) {
.container { padding: 10px; }
}
/* 常用断点 */
@media (max-width: 576px) { /* 手机 */ }
@media (min-width: 577px) and (max-width: 768px) { /* 平板竖屏 */ }
@media (min-width: 769px) and (max-width: 992px) { /* 平板横屏 */ }
@media (min-width: 993px) and (max-width: 1200px) { /* 桌面 */ }
@media (min-width: 1201px) { /* 大屏 */ }
/* 其他查询条件 */
@media (orientation: portrait) { /* 竖屏 */ }
@media (orientation: landscape) { /* 横屏 */ }
@media (hover: hover) { /* 支持 hover */ }
@media (prefers-color-scheme: dark) { /* 深色模式 */ }1.3 响应式单位
| 单位 | 说明 |
|---|---|
% | 相对父元素 |
vw/vh | 视口宽度/高度的 1% |
vmin/vmax | vw 和 vh 中的较小/较大值 |
rem | 相对根元素 font-size |
em | 相对当前元素 font-size |
二、移动端适配方案
2.1 rem 方案
javascript
// 动态设置根元素 font-size
function setRem() {
const designWidth = 750; // 设计稿宽度
const clientWidth = document.documentElement.clientWidth;
const rem = (clientWidth / designWidth) * 100;
document.documentElement.style.fontSize = rem + 'px';
}
window.addEventListener('resize', setRem);
setRem();css
/* 设计稿上 100px = 1rem */
.box {
width: 3.5rem; /* 设计稿 350px */
height: 1.2rem; /* 设计稿 120px */
}工具推荐: postcss-pxtorem 自动转换
2.2 vw 方案 (推荐)
css
/* 设计稿 750px 宽度,100vw = 750px */
/* 1px = 100/750 vw ≈ 0.1333vw */
.box {
width: 46.67vw; /* 设计稿 350px */
height: 16vw; /* 设计稿 120px */
}工具推荐: postcss-px-to-viewport 自动转换
javascript
// postcss.config.js
module.exports = {
plugins: {
'postcss-px-to-viewport': {
viewportWidth: 750,
unitPrecision: 5,
viewportUnit: 'vw',
minPixelValue: 1
}
}
}2.3 rem + vw 混合方案
css
/* 根元素使用 vw */
html {
font-size: calc(100vw / 7.5); /* 750 设计稿: 1rem = 100px */
}
/* 最大最小限制 */
@media (min-width: 750px) {
html { font-size: 100px; }
}
@media (max-width: 320px) {
html { font-size: 42.67px; }
}三、1px 问题
3.1 问题原因
在高 DPR (Device Pixel Ratio) 设备上,CSS 的 1px 会被渲染成多个物理像素,导致边框看起来偏粗。
3.2 解决方案
方案一:transform 缩放 (推荐)
css
.border-1px {
position: relative;
}
.border-1px::after {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 1px;
background: #000;
transform: scaleY(0.5);
transform-origin: 0 0;
}
/* 四边框 */
.border-1px-all::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 200%;
height: 200%;
border: 1px solid #000;
transform: scale(0.5);
transform-origin: 0 0;
box-sizing: border-box;
border-radius: 8px; /* 需要时加圆角 */
}方案二:媒体查询 + DPR
css
.border {
border: 1px solid #000;
}
@media (-webkit-min-device-pixel-ratio: 2) {
.border {
border-width: 0.5px;
}
}
@media (-webkit-min-device-pixel-ratio: 3) {
.border {
border-width: 0.333px;
}
}方案三:viewport 缩放
javascript
const dpr = window.devicePixelRatio || 1;
const scale = 1 / dpr;
const viewport = document.querySelector('meta[name="viewport"]');
viewport.content = `width=device-width,initial-scale=${scale},maximum-scale=${scale}`;
// 同时调整 rem 基准值
document.documentElement.style.fontSize = dpr * 100 + 'px';四、安全区域适配
4.1 iPhone 刘海屏适配
html
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">css
/* 使用 safe-area-inset-* 环境变量 */
.fixed-bottom {
padding-bottom: constant(safe-area-inset-bottom); /* iOS < 11.2 */
padding-bottom: env(safe-area-inset-bottom); /* iOS >= 11.2 */
}
/* 结合 calc */
.tabbar {
height: calc(50px + env(safe-area-inset-bottom));
padding-bottom: env(safe-area-inset-bottom);
}4.2 环境变量
css
env(safe-area-inset-top) /* 顶部安全距离 */
env(safe-area-inset-right) /* 右侧安全距离 */
env(safe-area-inset-bottom) /* 底部安全距离 (Home 指示条) */
env(safe-area-inset-left) /* 左侧安全距离 */五、图片适配
5.1 响应式图片
html
<!-- srcset + sizes -->
<img
src="small.jpg"
srcset="small.jpg 400w, medium.jpg 800w, large.jpg 1200w"
sizes="(max-width: 600px) 100vw, 50vw"
alt="响应式图片"
>
<!-- picture 元素 -->
<picture>
<source media="(max-width: 600px)" srcset="mobile.jpg">
<source media="(max-width: 1200px)" srcset="tablet.jpg">
<img src="desktop.jpg" alt="响应式图片">
</picture>5.2 高清屏图片
css
/* 使用 2x/3x 图片 */
.logo {
background-image: url('logo.png');
}
@media (-webkit-min-device-pixel-ratio: 2) {
.logo {
background-image: url('logo@2x.png');
}
}
/* 或使用 image-set */
.logo {
background-image: image-set(
url('logo.png') 1x,
url('logo@2x.png') 2x,
url('logo@3x.png') 3x
);
}六、高频面试题
Q1: rem 和 vw 方案的优缺点?
| 方案 | 优点 | 缺点 |
|---|---|---|
| rem | 兼容性好 | 需要 JS 动态计算 |
| vw | 纯 CSS,无需 JS | 旧浏览器不支持 |
Q2: 如何解决 1px 问题?
答案: 使用伪元素 + transform: scaleY(0.5) 是最通用的方案。
Q3: 什么是安全区域?如何适配?
答案: 安全区域是 iPhone X 等刘海屏设备的可视区域。使用 env(safe-area-inset-*) 环境变量来获取安全距离,并在固定定位元素上添加相应的 padding。