移动端适配方案(REM、Flexible等)
移动端适配的必要性
移动设备屏幕尺寸碎片化严重,从320px到414px甚至更大的屏幕宽度都有分布。传统的固定像素布局无法适应这种多样性,导致页面在某些设备上显示异常。移动端适配的核心目标是让页面在不同尺寸的屏幕上都能合理展示,保持统一的视觉体验。
视口(viewport)基础
适配方案的基础是对视口的正确理解。移动浏览器默认的视口宽度通常大于设备宽度,这会导致页面被缩放。通过meta标签可以控制视口行为:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
这个设置让视口宽度等于设备宽度,禁止用户缩放,为后续适配方案打下基础。没有正确的视口设置,任何适配方案都会失效。
REM适配方案原理
REM(root em)是相对于根元素(html)字体大小的单位。通过动态计算html的font-size,可以实现页面元素的等比缩放。基本公式为:
html font-size = (clientWidth / designWidth) * baseFontSize
假设设计稿宽度为750px,基础字体大小为100px,在375px宽度的设备上:
document.documentElement.style.fontSize = (375 / 750) * 100 + 'px';
这样1rem就等于50px,所有使用rem单位的元素都会按比例缩放。
Flexible.js实现细节
淘宝的Flexible方案是REM适配的经典实现。它解决了1px边框和高清屏适配问题。核心代码逻辑:
(function flexible(window, document) {
var docEl = document.documentElement
var dpr = window.devicePixelRatio || 1
// 调整body字体大小
function setBodyFontSize() {
if (document.body) {
document.body.style.fontSize = (12 * dpr) + 'px'
} else {
document.addEventListener('DOMContentLoaded', setBodyFontSize)
}
}
setBodyFontSize()
// 设置1rem = viewWidth / 10
function setRemUnit() {
var rem = docEl.clientWidth / 10
docEl.style.fontSize = rem + 'px'
}
setRemUnit()
// 监听页面大小变化
window.addEventListener('resize', setRemUnit)
window.addEventListener('pageshow', function(e) {
if (e.persisted) {
setRemUnit()
}
})
// 检测0.5px支持
if (dpr >= 2) {
var fakeBody = document.createElement('body')
var testElement = document.createElement('div')
testElement.style.border = '.5px solid transparent'
fakeBody.appendChild(testElement)
docEl.appendChild(fakeBody)
if (testElement.offsetHeight === 1) {
docEl.classList.add('hairlines')
}
docEl.removeChild(fakeBody)
}
})(window, document)
PostCSS插件辅助开发
手动计算rem值效率低下,可以使用PostCSS插件自动转换:
module.exports = {
plugins: {
'postcss-pxtorem': {
rootValue: 100, // 设计稿宽度/10
propList: ['*'],
selectorBlackList: ['.norem'] // 忽略norem开头的类名
}
}
}
这样在CSS中可以直接写设计稿尺寸:
.box {
width: 750px; /* 转换为7.5rem */
height: 200px; /* 转换为2rem */
}
1px边框解决方案
高清屏上1px物理像素可能对应多个设备像素,导致边框看起来过粗。Flexible方案通过transform缩放解决:
.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;
}
对于不同dpr的设备,可以针对性处理:
[data-dpr="2"] .border-1px::after {
transform: scaleY(0.5);
}
[data-dpr="3"] .border-1px::after {
transform: scaleY(0.333);
}
图片高清适配
根据设备dpr加载不同分辨率的图片:
<img src="image@2x.png"
srcset="image@1x.png 1x, image@2x.png 2x, image@3x.png 3x">
或者使用背景图配合媒体查询:
.icon {
background-image: url(image@1x.png);
}
@media (-webkit-min-device-pixel-ratio: 2) {
.icon {
background-image: url(image@2x.png);
background-size: 100% 100%;
}
}
弹性布局(Flex)配合REM
REM解决尺寸问题,Flex解决布局问题,两者结合效果更佳:
.container {
display: flex;
padding: 0.2rem;
}
.item {
flex: 1;
margin: 0.1rem;
height: 1rem;
}
动态REM计算优化
传统方案在超大屏幕上会放大过多,可以限制最大尺寸:
function setRem() {
const designWidth = 750;
const maxWidth = 768;
const clientWidth = Math.min(document.documentElement.clientWidth, maxWidth);
const rem = (clientWidth / designWidth) * 100;
document.documentElement.style.fontSize = rem + 'px';
}
响应式布局与REM结合
通过媒体查询调整REM基准值,实现更精细的控制:
@media screen and (min-width: 320px) {
html {
font-size: 42.6667px;
}
}
@media screen and (min-width: 360px) {
html {
font-size: 48px;
}
}
实际项目配置示例
完整webpack配置示例:
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
require('postcss-pxtorem')({
rootValue: 75,
propList: ['*'],
minPixelValue: 2
})
]
}
}
}
]
}
]
}
}
常见问题处理
字体大小问题:不希望文本随REM缩放,可以用媒体查询固定:
.title {
font-size: 16px;
}
@media (min-width: 375px) {
.title {
font-size: 18px;
}
}
第三方组件适配:有些UI库使用px单位,可以全局覆盖:
.third-party-component {
transform: scale(0.5);
transform-origin: 0 0;
}
现代CSS方案对比
CSS Viewport单位(vw/vh)逐渐成为新选择:
html {
font-size: 10vw; /* 视口宽度的10% */
}
.box {
width: 7.5rem; /* 等同于75vw */
}
但需要注意兼容性和计算精度问题。
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:触摸事件与手势识别