移动端适配原理及方案

1,589 阅读3分钟

大声地读出来我们移动端适配的核心

!!!移动端适配的核心目标是让同一网站在不同尺寸、不同分辨率的移动设备上都能提供良好的视觉和交互体验

<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`:初始缩放比例为1。
-   `maximum-scale=1.0, user-scalable=no`:禁止用户缩放(根据需求可选)。

1、移动端响应式布局的REM适配脚本

1.1 adapt 函数 - 获取默认字体大小

function adapt(designWidth, rem2px) {
  var d = window.document.createElement('p')
  d.style.width = '1rem'  // 创建一个宽度为1rem的元素
  d.style.display = "none" // 隐藏该元素,不影响页面显示
  var head = window.document.getElementsByTagName('head')[0]
  head.appendChild(d) // 将元素添加到head中
  var defaultFontSize = parseFloat(window.getComputedStyle(d, null).getPropertyValue('width'))
  return defaultFontSize // 返回浏览器默认的1rem对应的像素值
};

1.2 adapt 立即执行函数

!(function (doc, win, designWidth, rem2px) {
  // 参数说明:
  // doc = document, win = window
  // designWidth = 750 (设计稿宽度)
  // rem2px = 100 (1rem应该等于多少设计稿像素)
var docEl = doc.documentElement, // html根元素
  defaultFontSize = adapt(designWidth, rem2px), // 调用adapt获取默认字体大小
  resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize', // 判断设备支持的事件
```
```
recalc = function () {
  var clientWidth = win.innerWidth ||
    doc.documentElement.clientWidth ||
    doc.body.clientWidth // 获取视口宽度
  
  if (!clientWidth) return // 如果获取失败则退出
  
  if (clientWidth < 750) {
    // 核心计算公式:
    docEl.style.fontSize = clientWidth / designWidth * rem2px / defaultFontSize * 100 + '%'
  } else {
    // 屏幕宽度≥750px时,固定根字体大小为625%
    docEl.style.fontSize = '625%'
  }
}

2、移动端使用vw单位进行适配

2.1 纯CSS vw方案(推荐)

/* 移动端REM适配 - vw方案 */
html {
  /* 设计稿750px,1rem = 100px 的换算公式 */
  font-size: 13.333333vw; /* 100 / 750 * 100 = 13.333333 */
}

/* 大屏限制 - 超过750px时固定根字体大小 */
@media screen and (min-width: 750px) {
  html {
    font-size: 100px; /* 750px宽度时,13.333333vw = 100px */
  }
}

/* 可选:小屏最小字体限制,避免过小 */
@media screen and (max-width: 320px) {
  html {
    font-size: 42.666667px; /* 320px宽度时,13.333333vw ≈ 42.67px */
  }
}

body {
  /* 设置最大宽度,居中显示 */
  max-width: 750px;
  margin: 0 auto;
}

2.2 CSS + 少量JS(兼容性更好)

!(function (doc, win) {
  var docEl = doc.documentElement,
    resizeEvt = 'orientationchange' in win ? 'orientationchange' : 'resize',
    recalc = function () {
      var clientWidth = docEl.clientWidth || win.innerWidth;
      if (!clientWidth) return;
      
      // 使用vw单位,JS作为fallback
      if (clientWidth < 750) {
        // vw方案:13.333333vw
        docEl.style.fontSize = (clientWidth / 750 * 100) + 'px';
      } else {
        // 大屏固定
        docEl.style.fontSize = '100px';
      }
    };
  
  // 现代浏览器直接使用CSS vw,老旧浏览器使用JS计算
  if (!win.CSS || !CSS.supports || !CSS.supports('width', '1vw')) {
    if (!doc.addEventListener) return;
    win.addEventListener(resizeEvt, recalc, false);
    doc.addEventListener('DOMContentLoaded', recalc, false);
  }
})(document, window);
```
// 对应的css代码
```
/* 现代浏览器使用vw */
html {
  font-size: 13.333333vw;
}

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

/* JS会覆盖这个样式,所以不需要担心重复 */
.no-vw html {
  font-size: 16px; /* 默认值,会被JS覆盖 */
}

3 PostCSS插件自动转换(工程化推荐)

// postcss.config.js
module.exports = {
  plugins: {
    'postcss-px-to-viewport': {
      viewportWidth: 750, // 设计稿宽度
      viewportHeight: 1334, // 设计稿高度
      unitPrecision: 6, // 转换精度
      viewportUnit: 'vw', // 转换单位
      selectorBlackList: ['.ignore', '.hairlines'], // 忽略选择器
      minPixelValue: 1, // 最小转换值
      mediaQuery: false // 媒体查询中的px是否转换
    }
  }
}