h5 移动端适配最佳实践

移动端适配的方案需要根据具体的业务场景进行选择,工作中接触最多的是一些h5活动页、落地页等,这些页面在大小屏手机上的展示要求大小屏无差异,那么就针对以上要求进行项目整体的适配是最合适不过的。 如果是大屏手机展示更多的内容,并不着重于放大展示的话,外层布局使用vw,%,flex,内层直接px的混合适配的方案更加合适。

实现方案

方案一:手写vw+vh方案

 换算vw、vh函数的实现:

@mixin px-to-vw($px, $design-width: 375) { // $design-width:设计稿大小
  width: calc(#{$px} / #{$design-width} * 100vw);
}

.element {
  @include px-to-vw(50); /* 将 50px 转换为基于 375px 设计稿宽度的 vw */
}

方案二:手写rem+vw方案

换算rem函数的实现:

// 基础字体大小以vw为单位
$base-font-size: 0.43vw;

@function px-to-rem($px) {
  @return #{$px / $base-font-size}rem;
}

.element {
  font-size: px-to-rem(16);      // 结果是 1rem
  padding: px-to-rem(24) px-to-rem(32); // 结果是 1.5rem 2rem
}

方案三:postcss-pxtorem + amfe-flexible

postcss-pxtorem

借助第三方库 postcss-pxtorem 可以自动将px转换成rem,不需要手动计算,按照以下配置即可:

module.exports = {
  css: {
      postcss: {
        plugins: [
          require('postcss-pxtorem')({
            rootValue: 16, // 基准字体大小, 1rem = 16px
            unitPrecision: 3,  // 保留的小数位数
            propList: ['*'], // 需要转换的属性, * 表示全部属性
            minPixelValue: 1 // 设置最小的转换数值
          })
        ]
      }
    }
  }
 }

这一步实现了px到rem单位的转换,但是基准值 rootValue 写的16 (1rem = 16px) , 如果不动态调整font-size,页面将无法响应不同设备的尺寸变化。

amfe-flexible

借助第三方库 amfe-flexible 根据设备屏幕宽度大小自动调整font-size:

// Install
npm i -S amfe-flexible

// Import
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<script src="./node_modules/amfe-flexible/index.js"></script>

需要注意的是,amfe-flexible 计算 1rem 的方式是 设备屏幕宽度/10,源码如下所示 :

  // set 1rem = viewWidth / 10
  function setRemUnit () {
    var rem = docEl.clientWidth / 10
    docEl.style.fontSize = rem + 'px'
  }

 

那么需要将 postcss-pxtorem 中 rootValue 设置为 设计稿的宽度/10,假设设计稿的宽度大小是375,那么对应的rootValue(1rem)设置为 37.5

 
module.exports = {
  css: {
      postcss: {
        plugins: [
          require('postcss-pxtorem')({
            rootValue: 37.5, // 设计稿大小/10
     // ...
 }

此方案将屏幕拆分成10份,假设屏幕宽度是375,那么1rem = 37.5px,即 1px = 0.02666667rem 那么反过来计算元素大小为37.5px时0.02666667rem x 37.5 = 1.000000012 会存在轻微的误差。 

 

方案四:postcss-pxtorem + 手写实现flexible.js

 针对 amfe-flexible 存在的问题,可以自己设置基准值,例如 1rem = 50px:

  function setRootPixel() {
    var defaultFontSize = 0;

    function getDefaultFontSize() {
      if (defaultFontSize) {
        return defaultFontSize;
      }

      document.documentElement.style.fontSize = "";
      var temp = document.createElement("div");
      temp.style.cssText = "width:1rem;display:none";
      document.head.appendChild(temp);
      defaultFontSize =
        +window
          .getComputedStyle(temp, null)
          .getPropertyValue("width")
          .replace("px", "") || 16;
      document.head.removeChild(temp);

      return defaultFontSize;
    }

    function setRootFontSize() {
    // 预设的基准值,此处的值与rootValue相等即可
      var rem2px = 50;
      var clientWidth =
        window.innerWidth && document.documentElement.clientWidth
          ? Math.min(window.innerWidth, document.documentElement.clientWidth)
          : window.innerWidth ||
            document.documentElement.clientWidth ||
            (document.body && document.body.clientWidth) ||
            375;
    // 在设计稿是375的情况下1rem = 50px,此处则根据实际的clientWidth来计算根元素 `<html>` 的 `font-size` 值
      var htmlFontSizePx = (clientWidth / 375) * rem2px;

      window.ROOT_FONT_SIZE = htmlFontSizePx;

      var htmlRootElement = document.querySelector("html");
      if (htmlRootElement) {
      // 通过相对于原来字体百分比大小的形式来设置字体大小
        htmlRootElement.style.fontSize =
          (htmlFontSizePx / getDefaultFontSize()) * 100 + "%";
      }
    }

    function adjust(immediate) {
      if (immediate) {
        setRootFontSize();
        return;
      }

      setTimeout(setRootFontSize, 30);
    }

    adjust(true);

    window.addEventListener("resize", adjust, false);

    if ("onorientationchange" in window) {
      window.addEventListener("orientationchange", adjust, false);
    }
  }

  typeof window !== "undefined" && setRootPixel();

假设rootValue为50(1rem = 50px),那么flexible中划分的份数 = 设计稿大小/50 ,再通过 实际屏幕大小/划分的份数计算出根元素 <html> 的 font-size 值。 

总结

两种方案都使用了postcss-pxtorem,都是在编译时对单位进行了转换,在实际的业务场景中会通过js设置动态样式,自行实现转换函数:

 

/**
 * px转rem
 * @param px 375屏宽下的px值
 * @param unit 是否需要rem单位,默认true
 * @returns rem值
 */

 export function px2rem(px: number, unit?: true): string;
 export function px2rem(px: number, unit: false): number;
 export function px2rem(px: number, unit = true) {
     const rem = Math.floor((px / 50) * 1000000) / 1000000;
     if (unit) {
         return `${rem}rem`;
     }
 
     return rem;
 }
作者:吃嘛嘛香呼呼
链接:https://juejin.cn/post/7408798040520556559
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
posted @ 2024-10-29 13:26  上官靖宇  阅读(20)  评论(0编辑  收藏  举报