移动端布局单位适配方案详解

248 阅读4分钟

1. 视口(Viewport)设置

<!-- 标准视口设置 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

2. 常用适配方案

2.1 rem 适配方案

/* 基准值设置 */
html {
  font-size: 16px; /* 默认值 */
}

/* 媒体查询设置不同屏幕尺寸的基准值 */
@media screen and (min-width: 320px) {
  html {
    font-size: 16px;
  }
}

@media screen and (min-width: 375px) {
  html {
    font-size: 18.75px;
  }
}

@media screen and (min-width: 414px) {
  html {
    font-size: 20.7px;
  }
}

/* 使用示例 */
.box {
  width: 10rem;    /* 在 375px 屏幕上为 187.5px */
  height: 5rem;    /* 在 375px 屏幕上为 93.75px */
  font-size: 0.8rem; /* 在 375px 屏幕上为 15px */
}

2.2 vw/vh 适配方案

/* 使用 vw 单位 */
.box {
  width: 50vw;     /* 视口宽度的 50% */
  height: 30vh;    /* 视口高度的 30% */
  font-size: 4vw;  /* 视口宽度的 4% */
}

/* 结合 calc() 使用 */
.box {
  width: calc(50vw - 20px);
  height: calc(30vh + 10px);
}

2.3 百分比适配方案

.container {
  width: 100%;
  padding: 0 15px;
}

.box {
  width: 50%;     /* 父元素宽度的 50% */
  height: 30%;    /* 父元素高度的 30% */
  margin: 2%;     /* 父元素宽度的 2% */
}

3. 实际应用方案

3.1 淘宝 flexible.js 方案

// flexible.js 核心代码
(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();

  // 设置 rem 单位
  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()
    }
  })
})(window, document)

3.2 postcss-pxtorem 方案

// postcss.config.js
module.exports = {
  plugins: {
    'postcss-pxtorem': {
      rootValue: 37.5, // 设计稿宽度/10
      propList: ['*'],
      selectorBlackList: ['.norem'] // 过滤掉 .norem 开头的 class
    }
  }
}

3.3 混合使用方案

/* 混合使用 rem 和 vw */
.container {
  width: 100%;
  padding: 0 0.4rem; /* 使用 rem */
}

.box {
  width: 50vw;      /* 使用 vw */
  height: 2rem;     /* 使用 rem */
  margin: 0.2rem;   /* 使用 rem */
  font-size: 0.28rem; /* 使用 rem */
}

/* 响应式布局 */
@media screen and (max-width: 320px) {
  .box {
    width: 100%;
  }
}

4. 特殊场景处理

4.1 1px 边框问题

/* 使用伪元素 + transform 实现 1px 边框 */
.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;
}

4.2 图片适配

/* 图片自适应 */
.img-container {
  width: 100%;
  height: 0;
  padding-bottom: 56.25%; /* 16:9 比例 */
  position: relative;
}

.img-container img {
  position: absolute;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

4.3 文字大小适配

/* 使用 clamp() 函数控制文字大小范围 */
.text {
  font-size: clamp(12px, 4vw, 16px);
}

/* 使用媒体查询控制文字大小 */
@media screen and (max-width: 320px) {
  .text {
    font-size: 12px;
  }
}

@media screen and (min-width: 321px) and (max-width: 414px) {
  .text {
    font-size: 14px;
  }
}

@media screen and (min-width: 415px) {
  .text {
    font-size: 16px;
  }
}

5. 最佳实践建议

1 选择合适的适配方案

  • 小型项目:使用媒体查询 + 百分比

  • 中型项目:使用 rem + 媒体查询

  • 大型项目:使用 flexible.js 或 postcss-pxtorem

2 统一设计稿尺寸

  • 建议使用 375px 或 750px 作为设计稿基准宽度

  • 保持设计稿与开发环境的一致性

3 合理使用单位

  • 容器宽度:使用百分比或 vw

  • 字体大小:使用 rem

  • 间距:使用 rem

  • 边框:使用 px

4. 注意性能优化

  • 避免频繁的尺寸计算

  • 使用 transform 代替位置变化

  • 合理使用缓存

  • 测试和调试

  • 使用 Chrome 开发者工具模拟不同设备

  • 使用真机测试

  • 关注页面加载性能

6. 常见问题解决方案

6.1 横屏适配

/* 横屏样式调整 */
@media screen and (orientation: landscape) {
  .container {
    width: 100vh;
    height: 100vw;
    transform: rotate(90deg);
    transform-origin: 0 0;
  }
}

6.2 安全区域适配

/* 适配 iPhone X 等全面屏设备 */
.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);
}

6.3 高清屏适配

/* 使用媒体查询适配不同 DPR */
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 2dppx) {
  .image {
    background-image: url('image@2x.png');
  }
}

@media (-webkit-min-device-pixel-ratio: 3), (min-resolution: 3dppx) {
  .image {
    background-image: url('image@3x.png');
  }
}

淘宝更改

(function flexible(window, document) {
  var docEl = document.documentElement
  var dpr = window.devicePixelRatio || 1

  // 调整 body 标签的 font-size
  function setBodyFontSize() {
    if (document.body) {
      document.body.style.fontSize = (12 * dpr) + 'px'
    } else {
      document.addEventListener('DOMContentLoaded', setBodyFontSize)
    }
  }
  setBodyFontSize()

  // 设置 rem 单位
  function setRemUnit() {
    // 修改这里:将屏幕宽度分成 7.5 份,这样 1rem = 100px
    var rem = docEl.clientWidth / 7.5
    docEl.style.fontSize = rem + 'px'
  }

  setRemUnit()

  // 监听页面大小变化
  window.addEventListener('resize', setRemUnit)
  window.addEventListener('pageshow', function(e) {
    if (e.persisted) {
      setRemUnit()
    }
  })
})(window, document)
/* 示例 */
.header {
    height: 0.88rem;    /* 设计稿 88px */
    line-height: 0.88rem;
    font-size: 0.28rem; /* 设计稿 28px */
    padding: 0 0.3rem;  /* 设计稿 30px */
}

.btn {
    width: 3.2rem;      /* 设计稿 320px */
    height: 0.88rem;    /* 设计稿 88px */
    font-size: 0.32rem; /* 设计稿 32px */
    border-radius: 0.44rem; /* 设计稿 44px */
}

使用 PostCSS 插件自动转换

为了进一步简化开发,可以使用 postcss-pxtorem 插件自动将 px 转换为 rem

// postcss.config.js
module.exports = {
  plugins: {
    'postcss-pxtorem': {
      rootValue: 100,    // 设计稿宽度/7.5
      propList: ['*'],
      selectorBlackList: ['.norem'] // 过滤掉 .norem 开头的 class
    }
  }
}