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
相关推荐
muddjsv几秒前
CSS 高级选择器精讲:伪类、伪元素、逻辑伪类与选择器解耦规范
前端·css
HH‘HH2 分钟前
前端应用的离线暂停更新策略:原理、实现与最佳实践
开发语言·前端·php
软件开发技术深度爱好者7 分钟前
HTML5实现数学函数画图器
前端·javascript·html5
牧艺14 分钟前
cos-design BubbleField:用 Canvas 做一个「会呼吸」的深海气泡场
前端·webgl·canvas
随风一样自由23 分钟前
【前端+登录页】登录页背景性能优化实战:从 722 KB 到 200 KB,LCP 从 2.5s 到 1.2s
前端·性能优化·登录页·ttl·lcp
swipe24 分钟前
09|(前端转全栈)商品为什么不能随便上下架?后端状态机思维入门
前端·后端·全栈
taocarts_bidfans28 分钟前
Taoify 站点访问地区限制与 IP 管控配置
前端·javascript·tcp/ip·taoify
狂师33 分钟前
用自然语言控制手机,一条命令让 AI 帮你操作 Android 和 iOS 设备!
前端·开源·测试
Csvn42 分钟前
# 🎨 CSS Container Queries 实战踩坑——从「响应式」到「容器式」
前端
zhangjw3442 分钟前
第36篇:Spring Boot进阶:Web开发+参数校验+全局异常处理
前端·spring boot·后端