h5移动端rem适配方案注意事项

rem适配方案原理

  • 以根html元素的font-size为基准大小,来设置页面元素的尺寸,字体大小等
  • 当页面大小发生变化时,同步修改根html元素的font-size大小,这样凡属rem单位的实际px也会跟着适配改变

谨慎使用amfe-flexible+postcss-pxtorem适配方案

简述amfe-flexible源码与postcss-pxtorem配置方式

在网上找rem适配方案,很容易就会找到以此组合实现的相关文章。本质上没问题,确实是依据rem适配方案原理来的。但amfe-flexible是将视口宽度分为10等分,如果你是基于375宽度的模式调试开发,那么此时根html的字体大小被设置成了37.5,你需要将postcss-pxtorem中的根字体大小也设置成37.5,然后打包时postcss-pxtorem会帮你自动将项目中的px转换为对应的rem值。amfe-flexible会自动帮你调整根html的字体大小

amfe-flexible源码

js 复制代码
(function flexible (window, document) {
  var docEl = document.documentElement
  var dpr = window.devicePixelRatio || 1

  // adjust body font size
  function setBodyFontSize () {
    if (document.body) {
      document.body.style.fontSize = (12 * dpr) + 'px'
    }
    else {
      document.addEventListener('DOMContentLoaded', setBodyFontSize)
    }
  }
  setBodyFontSize();

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

  setRemUnit()

  // reset rem unit on page resize
  window.addEventListener('resize', setRemUnit)
  window.addEventListener('pageshow', function (e) {
    if (e.persisted) {
      setRemUnit()
    }
  })

  // detect 0.5px supports
  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))

为什么需要谨慎使用此方案?

因为以此方案适配出来的界面,即使在设计稿的视口宽高下,适配出来的效果图也存在一定的偏差。

如:375px宽度的设计稿,在375px宽度的视口下,那么amfe-flexible算出的1rem的宽度为37.5px,反过来,1px就等于1 rem / 37.5 px ≈ 0.026666666 rem,10px计算方式为 0.026666666 rem * 10 ≈ 0.26rem,计算结果全是约等于,那自然无法完美还原设计稿。

amfe-flexible源码改进

amfe-flexible的问题在于,1rem计算出来的px值与设计稿的尺寸无法整除,那如果1rem计算出来的px值为10px或100px这种,那就无问题了。

还是以之前375px宽度的设计稿,在375px宽度的视口,还原10px为例。根据改进后的amfe-flexible,以10px为基准字体大小(即:在375设计稿,以及375px的视口宽度下,1rem为10px,1px为0.1rem),那么10px转换为rem为1rem,15px为1.5rem,得到的都是确定的值,因此至少在与设计稿同样宽度的浏览器视口下,是能完美还原设计稿的。

使用改进后的amfe-flexible源码postcss-pxtorem如何设置,无论实际的设计稿宽度是多少,postcss-pxtoremrootValue设置为10即可

重点在setRemUnit方法

ts 复制代码
// set 1rem = viewWidth / 10
function setRemUnit() {
    // 设计稿宽度(需要设置为你实际拿到的ui稿宽度)
    const uiDesignWidth = 375 
    // 当前浏览器视口宽度相当于设计稿宽度的缩放比 = 屏幕宽度/设计稿宽度
    const scale = docEl.clientWidth / uiDesignWidth
    // 基准字体大小(同时也表示1rem对应多少px),无论你实际的设计稿宽度是多少,这个baseFontSize都不需要变
    // P.S. postcss-pxtorem的rootValue值,需要与baseFontSize一致
    const baseFontSize = 10
    // 实际根html元素字体大小 = 基准字体大小 * 当前浏览器视口宽度相当于设计稿宽度的缩放比
    const oneRemPx = baseFontSize * scale
    // 设置根html元素字体大小(即: 1rem等于多少px)
    docEl.style.fontSize = oneRemPx + 'px'
}

改造后的amfe-flexible源码

flexible.ts

ts 复制代码
(function flexible (window, document) {
  var docEl = document.documentElement
  var dpr = window.devicePixelRatio || 1

  // adjust body font size
  function setBodyFontSize () {
    if (document.body) {
      document.body.style.fontSize = (12 * dpr) + 'px'
    }
    else {
      document.addEventListener('DOMContentLoaded', setBodyFontSize)
    }
  }
  setBodyFontSize();

  // set 1rem = viewWidth / 10
  function setRemUnit () {
    // 设计稿宽度(需要设置为你实际拿到的ui稿宽度)
    const uiDesignWidth = 375
    // 当前浏览器视口宽度相当于设计稿宽度的缩放比 = 屏幕宽度/设计稿宽度
    const scale = docEl.clientWidth / uiDesignWidth
    // 基准字体大小(同时也表示1rem对应多少px),无论你实际的设计稿宽度是多少,这个baseFontSize都不需要变
    // P.S. postcss-pxtorem的rootValue值,需要与baseFontSize一致
    const baseFontSize = 10
    // 实际根html元素字体大小 = 基准字体大小 * 当前浏览器视口宽度相当于设计稿宽度的缩放比
    const oneRemPx = baseFontSize * scale
    // 设置根html元素字体大小(即: 1rem等于多少px)
    docEl.style.fontSize = oneRemPx + 'px'
  }

  setRemUnit()

  // reset rem unit on page resize
  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')
    // 将测试元素设置0.5px的边框
    testElement.style.border = '.5px solid transparent'
    fakeBody.appendChild(testElement)
    docEl.appendChild(fakeBody)
    if (testElement.offsetHeight === 1) {
      // 检测测试元素的高度,如果高度为1px,那说明浏览器不支持直接显示0.5px的元素,凡属小于1px的元素都当作1px渲染,此时在根html标签添加`hairlines`类,css或js就可以通过判断根html中是否有该class来,判定浏览器是否支持直接渲染0.5px,有该class就表示不支持,否则表示支持
      docEl.classList.add('hairlines')
    }
    // 删除测试元素
    docEl.removeChild(fakeBody)
  }
}(window, document))

扩展

vw或vh适配方案也有这种问题吗?

是的,也有同样的问题。vw或vh,实际就是将视口分成了100份,而原始的amfe-flexible, 是将视口分成了10份. 因此采用vh,vw方案,即使浏览器视口与设计稿宽度一致,也无法完美还原设计稿

依然以375px设计稿和视口宽度为例

ini 复制代码
100vw = 375px
1vw = 375px / 100 = 37.5px
1px ≈ 1vw / 37.5 ≈ 0.02666 vw
相关推荐
前端啊龙1 分钟前
用vue3封装丶高仿element-plus里面的日期联级选择器,日期选择器
前端·javascript·vue.js
一颗松鼠5 分钟前
JavaScript 闭包是什么?简单到看完就理解!
开发语言·前端·javascript·ecmascript
小远yyds25 分钟前
前端Web用户 token 持久化
开发语言·前端·javascript·vue.js
吕彬-前端1 小时前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱1 小时前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai2 小时前
uniapp
前端·javascript·vue.js·uni-app
bysking2 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
王哲晓3 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
fg_4113 小时前
无网络安装ionic和运行
前端·npm
理想不理想v3 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试