出处:移动 web 适配利器-rem

原作者:Tencent AlloyTeam


提示

本文原作者写于 2016 年,所以文中内容有些地方比较过时,注意判断

rem 介绍

rem 是 font size of the root element,意思就是根据网页的根元素来设置字体大小,和 em (font size of the element) 的区别是,em 是根据其父元素的字体大小来设置,而 rem 是根据网页的根元素 (html、:root) 来设置字体大小的

rem 不仅可以适用于字体,同样可以用于 widthheightmargin 等这些样式的单位

rem 屏幕适配

在讲 rem 屏幕适配之前,先说一下一般做移动端适配的方法,一般可以分为: 

  1. 简单一点的页面,一般高度直接设置成固定值,宽度一般撑满整个屏幕
  2. 稍复杂一些的是利用百分比设置元素的大小来进行适配,或者利用 flex 等 css 去设置一些需要定制的宽度
  3. 再复杂一些的响应式页面,需要利用 CSS3 的 media query 属性来进行适配,大致思路是根据屏幕不同大小,来设置对应的 CSS 样式

上面的一些方法,其实也可以解决屏幕适配等问题

px2rem

如果利用 rem 来设置 CSS 的值,一般要通过一层计算才行,比如如果要设置一个长宽为 100pxdiv,那么就需要计算出 100px 对应的 rem 值是 100 / 16 = 6.25rem

举例写一个 SCSS 的函数 px2rem

@function px2rem($px) {
    $rem: 37.5px;
    @return ($px/$rem) + rem;
}

看到这里,你可能会发现一些不理解的地方,就是上面那个 $rem: 37.5px; 是怎么来的,正常情况下不是默认的 16px 么,这个其实就是页面的基准值,和 html 的 font-size 有关

rem 基准值计算

关于 rem 的基准值,也就是上面那个 37.5px 其实是根据我们所拿到的视觉稿来决定的

由于所写出的页面是要在不同的屏幕大小设备上运行的,所以在写样式的时候必须要先以一个确定的屏幕来作为参考,这个就由我们拿到的视觉稿来定。假如拿到的视觉稿是以 iphone6 的屏幕为基准设计的,iPhone6 的屏幕大小是 375pxrem = window.innerWidth / 10

这样计算出来的 rem 基准值就是 37.5,这里为什么要除以 10 呢,其实这个值是随便定义的,因为不想让 html 的 font-size 太大,当然也可以选择不除,只要在后面动态 JS 计算时保证一样的值就可以,在这里列举一下其他手机的:

  • iphone3gs:320px / 10 = 32px
  • iphone4/5:320px  / 10 = 32px

动态设置 html 的 font-size

现在关键问题来了,该如何通过不同的屏幕去动态设置 html 的 font-size 呢,这里一般分为两种办法

  1. 利用 CSS 的 media query 来设置
@media (min-device-width : 375px) and (max-device-width : 667px) and (-webkit-min-device-pixel-ratio : 2) {
      html { font-size: 37.5px; }
}
  1. 利用 JS 来动态设置
document.getElementsByTagName('html')[0].style.fontSize = window.innerWidth / 10 + 'px';

由此可以通过设置不同的 html 基础值来达到在不同页面适配的目的,当然在使用 JS 设置时,需要绑定页面的 resize 事件来达到变化时更新 html 的 font-size

rem 适配进阶

一般我们获取到的视觉稿大部分是 iphone6 的,所以我们看到的尺寸一般是双倍大小的,在使用 rem 之前,我们一般会自觉的将标注 / 2,其实这也并无道理,但是当我们配合 rem 使用时,完全可以按照视觉稿上的尺寸来设置

  1. 设计给的稿子双倍的原因是 iphone6 这种屏幕属于高清屏,也即是设备像素比 dpr (device pixel ratio) 比较大,所以显示的像素较为清晰
  2. 一般手机的 dpr 是 1,iphone4,iphone5 这种高清屏是 2,iphone6s plus 这种高清屏是 3,可以通过 js 的 window.devicePixelRatio 获取到当前设备的 dpr
  3. 拿到了 dpr 之后,我们就可以在 viewport meta 头里,取消让浏览器自动缩放页面,而自己去设置 viewport 的 content。例如:
// 这里之所以要设置 viewport 是因为要实现 border 1px 的效果,加入我给 border 设置了 1px,在 scale 的影响下,高清屏中就会显示成 0.5px 的效果
meta.setAttribute('content', 'initial-scale=' + 1/dpr + ', maximum-scale=' + 1/dpr + ', minimum-scale=' + 1/dpr + ', user-scalable=no');
  1. 设置完之后配合 rem,修改:
@function px2rem($px) {
    $rem: 75px;
    @return ($px/$rem) + rem;
}

双倍 75,这样就可以完全按照视觉稿上的尺寸来了。不用在 / 2 了,这样做的好处是:

  1. 解决了图片高清问题
  2. 解决了 border 1px 问题(我们设置的 1px,在 iphone 上,由于 viewport 的 scale 是 0.5,所以就自然缩放成 0.5px)

但是 rem 也并不是万能的,下面也有一些场景是不适于使用 rem 的:

  1. 当用作图片或者一些不能缩放的展示时,必须要使用固定的 px 值,因为缩放可能会导致图片压缩变形等