移动端REM布局适配

记录一下我公司的移动端REM布局适配方案

Posted by ddxg on September 1, 2019

移动端REM布局适配

在公司大部分需求都是做移动端的H5页面,APP内嵌的H5 和 移动端WAP 站。这里总结一下我公司用的REM适配方法,再拓展一下其他方法。

rem 、 em 和 px

  • px:绝对长度单位,与显示设备相关,对于屏幕显示,通常是一个设备像素(点)的显示。pc端的页面一般使用px作为css长度单位。
  • em:相对长度单位,这个单位表示元素的font-size的计算值它是相对父元素而言的。值是一个倍数。浏览器的默认字体高都是 16px,未经调整的浏览器显示 1em = 16px。
  • rem: 相对长度单位,这个单位代表根元素(<html>)的font-size大小。值是一个倍数。

移动端页面显然不适用px作为长度单位,手机的尺寸和分辨率不尽相同,页面难以做到统一。

en单位有个问题,就是它是相对于其父级字体的倍数的,当出现有多重嵌套内容时,计算会变得很复杂,可能改变一个元素的字体大小,则子元素的尺寸都会跟着变化,需要重新计算。

rem 就不会有这个问题,因为它始终是基于根元素(<html>)的。

所以,rem是比较适合移动端的。

正在使用的方法

rem布局的本质是等比缩放,一般是基于宽度。最重要的是要给根元素(<html>)动态设置多少的font-size合适。 一般是将设计稿的宽度等分为100份,然后将1份大小的px值设置为<html>元素的font-size大小。

比如,设计稿的宽度是750px,分成100份之后每份的大小为7.5px,即设置<html>元素的font-size大小为7.5px。在元素中1rem = 7.5px。 如果设计稿中有一个宽20px的块,那在样式中就需要换算一下啦,width:2.667rem。这里的转换看以来有点复杂,不过我们可以使用css预处理器或者gulp、webpack构建的时候进行转换。

1 rem = 7.5px;
x rem = 20px;

// 由比例关系可以得出:
x = 1 * 20 / 7.5

下面是我在公司一直在用的rem处理的代码,设置<html>font-size的地方不是很明白其用意是什么,不过知道上面的转化规则,font-size设置成多少,都能转化px2rem。

// 只适用于移动端,没有处理pc端的显示,在pc端页面会显示地很大。
(function(window, html) {
    // 默认的设计稿宽度720px
    var designWidth = 720;

    function recalc() {
        //var windowWidth = html.clientWidth < designWidth ? html.clientWidth : designWidth;
        // 获取页面的宽度
        var windowWidth = html.clientWidth;
        
        // 我的理解是: 将页面等分成 designWidth 份
        // 这样的话,设计稿中的1px 就等于 1rem,方便转换px和rem
        // 后面又 *100 ,我觉得是确保计算出来的值是非小数,保证之后的计算精度。
        //  *100 之后,则样式中rem的值就需要相应的缩小100背
        //  即:设计稿中的20px,在样式中就要写成0.2rem
        var fontSize = windowWidth / designWidth * 100;

        setFontSize(fontSize);

        var completedFontSize = getFontSize();
        
        // 如果实际 fontSize 与设置的 fontSize 不等,则根据比例,重新计算
        // 存在浏览器自动放大font-size的问题
        // 参考:https://www.cnblogs.com/keaizhouzhou/p/6702551.html
        if (Math.abs(completedFontSize - fontSize) > 0.1) {
            setFontSize(fontSize * fontSize / completedFontSize);
        }
    }

    function setFontSize(fontSize) {
        html.style.fontSize = fontSize + 'px';
    }

    function getFontSize() {
        return parseFloat(window.getComputedStyle(html)['font-size']);
    }

    window.calcRem = function(width) {
        designWidth = width;
        recalc();
        // 监听resize
        window.addEventListener('resize', recalc);
    }
}(window, document.documentElement));

在window上挂了一个calcRem方法,如果设计稿的宽度是750px的,则执行calcRem(750)。代码最好放在html中css加载之前执行。

上面的注释是我的理解,我觉得var fontSize = windowWidth / designWidth * 100; 这个算式是简化后得来的,具体怎么简化的我还真的想不明白。或许是考虑到了简化px到rem的转化计算问题。 不过,根据rem的定义,我们也很容易知道样式中的rem的值要怎么写。

px2rem 方法

css预处理器

我是用的是stylus预处理器,可以定义一个方法:

// px转换rem的方法,这里的100是根据动态设置的html的font-size设置的
px2rem(pxValue) {
    unit(pxValue / 100, rem);
}
// 样式中就可以使用方法,20和30都是设计稿的尺寸,写代码的时候就不用手动转换了
.sign-box {
    padding: px2rem(20) px2rem(30);
}

sass-rem mixin-rem

gulp 构建时转换

postcss-px2rem

webpack 构建时转换

postcss-px2rem

// vue.config.js
// remUnit是指1rem等于多少px的值,我这里1rem = 100px,则 remUnit为100
css: {
    loaderOptions: {
        postcss: {
            plugins: [require('postcss-px2rem')({
                remUnit: 100
            })]
        }
    }
}

参考:

px2rem-loader

// vue.config.js
chainWebpack: config => {
    config.optimization.splitChunks({
        cacheGroups: {
            vendors: {
                name: 'chunk-vendors',
                minChunks: 2,
                chunks: 'initial'
            }
        }
    });

    config.module
        .rule('stylus')
        .oneOf('vue')
        .use('px2rem-loader')
        .loader('px2rem-loader')
        .before('postcss-loader') // this makes it work.
        .options({ remUnit: 100, remPrecision: 8 })
        .end()
}

参考:

待学习

不懂的东西还特别多,文中有错误的地方麻烦指出。

参考: