阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 移动端适配方案(REM、Flexible等)

移动端适配方案(REM、Flexible等)

作者:陈川 阅读数:13605人阅读 分类: HTML

移动端适配的必要性

移动设备屏幕尺寸碎片化严重,从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

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌