H5适配知多少

139 阅读4分钟

  对于H5页面来说,适配是一个必须要处理的问题,毕竟有这么多手机品牌和型号,它们的尺寸和分辨率不尽相同,而UI设计稿只有一种尺寸。为了保证我们的H5页面,在不同的手机上都能按照UI设计稿展示,我们需要对页面做一定的处理,把px这种绝对长度 (1px = 1/96th of 1in) 的单位,转变成一种相对长度的单位。

通常来说有2种方式,一种是px -> rem,另一种是px -> vw

px to rem

概括地说,rem 单位的意思是“根元素的字体大小(“根 em”的 rem 标准),根元素就是<html>元素。设置html元素的fontSize:document.documentElement.style.fontSize = '16px',这样我们就可以使用1rem来表示16px

假如设计稿的尺寸是375px,我们可以把根元素的fontSize设置为37.5px(也就是分成10份)。这时候假如有个16px的padding,使用rem表示的话就是:16/37.5 = 0.42667rem。设计稿上全是px单位,我们要手动去计算,除数还是37.5这种口算不好算的,结果还除不尽,这就很难受了。

有一种偷懒的方法,是把1rem设置为100px,这样就很好计算了。通常在一些web系统中,可能只需要对某些页面做适配,会用到这种方法,只有要适配的页面使用rem单位,其他还是px。

rem通常需要一个基准值,比如就是100(rootValue),然后需要根据document.documentElement.clientWidth / UIWidth * 100,这个其实很好理解。我们通常所说的分成10份,也只是把基准值,设为了UIWidth / 10 罢了(rootValue = UIWidth/10)。

amfe-flexible这个库就是帮我们设置rem的,里面的代码也很简单。

H5页面中,需要把所有的px都转成rem,这时候如果还是手动去处理的话,那就太麻烦了。所以这时候就要借助一些插件,这就是postcss-pxtorem

// vite.config.ts
import pxtorem from 'postcss-pxtorem'
export default defineConfig({
    // ...
    css: {
        postcss: {
            plugins: [
                pxtorem({
                    rootValue: 37.5, // 如果同时使用了amfe-flexible,rootValue就只能设置为UIWidth/10
                    // 当然也可以把amfe-flexible的代码copy下来自己改rem
                    propList: ['*'],
                    mediaQuery: true
                })
            ]
        }
    }
    // ...
})

如果使用一个单独的文件postcss.config.js进行配置的话,postcss的值是这个config的地址

image.png

px -> vw

amfe-flexible的README.md中写道:

由于viewport单位得到众多浏览器的兼容,lib-flexible这个过渡方案已经可以放弃使用,不管是现在的版本还是以前的版本,都存有一定的问题。建议大家开始使用viewport来替代此方法。

不是说rem就淘汰了,只是说我们也可以使用vw来实现适配。

  1. 由于我们的设计稿是750的,但是vant的设计稿是375的,所以需要对业务页面和vant做不同的处理:
// 业务页面
const pxtovw = pxtovw({
    viewportWidth: 750,
    viewportUnit: 'vw',
    mediaQuery: true,
    exclude: /node_modules\/vant/i // 不包括vant
})
// vant
const pxtovw_vant = pxtovx({
    viewportWidth: 375,
    viewportUnit: 'vw'
    exclude: /^(?!.*node_modules\/vant/ // 只包含vant
})

  1. 我们的某些H5页面是横屏展示的,所以还得对这些页面做处理:
// 横屏页面
const pxtovw_landscape = pxtovw({
    viewportWidth: 750,
    viewportUnit: 'vw',
    landscape: true,
    landscapeWidth: 1624,
    exclude: /^(?!.*\/(landscapePage1|landscapePage2))/ 
    // 这时候需要把page1和page2加到业务页面的exclude中
    // exclude: /node_modules\/vant|LandscapePage1|LandscapePage2/i
})

由于npm上的包中没有include功能,所以要实现只匹配某些页面,参考上述写法;使用数组写法的话(感觉好像是匹配page1和page2),在exclude中会导致都无法匹配,可以看下源码的实现。

使用landscape的时候会报错,源码中使用了postcss.atRule,但postcss中只有AtRule,所以还需要手动赋值:postcss.atRule=postcss.AtRule(可能postcss老的版本中是atRule,毕竟pxtovw上次提交都5年前了)。

github上的postcss-px-to-viewport中是有include功能的,如果你觉得使用exclude实现include功能不是很好理解,可以直接pnpm add https://github.com/evrone/postcss-px-to-viewport.git -D安装有include功能的版本。

注意: 内嵌(直接写在元素style中的)px不能被转换,所以在使用某些支持size的vant组件时,size就不能用了,因为生成的样式是内嵌的(懒省事也可以用,会有一些误差罢了)。
1px: 默认<=1px是不转换的,1px(通常是边框)在现在的手机上看起来是比较粗的,一种方法是设置为0.5px,一种是换一种浅一点的颜色,再一种是scale(0.5)。我是怎么简单怎么来😏

总结

1. pxtorem和pxtovw都能实现我们的需求,选择哪种方式都可以
2. pxtorem需要额外引入类似amfe-flexible的包或自己实现
3. pxtovw提供了横屏适配,通过landscape:true启用,实现简单
4. 由于H5设计稿通常是375或750,有因数3,除不尽,pxtovw肯定会产生误差,具体表现因手机而异,虽然实际上不大能看出来;pxtorem可以通过设置可以除尽的rootValue规避(仅限因数3手机,因为在设置rem时也要除以375|750)