原作者:Tencent AlloyTeam
提示
本文原作者写于 2016 年,所以文中内容有些地方比较过时,注意判断
rem
介绍
rem
是 font size of the root element,意思就是根据网页的根元素来设置字体大小,和 em
(font size of the element) 的区别是,em
是根据其父元素的字体大小来设置,而 rem
是根据网页的根元素 (html、:root
) 来设置字体大小的
rem
不仅可以适用于字体,同样可以用于 width
、height
、margin
等这些样式的单位
rem
屏幕适配
在讲 rem
屏幕适配之前,先说一下一般做移动端适配的方法,一般可以分为:
- 简单一点的页面,一般高度直接设置成固定值,宽度一般撑满整个屏幕
- 稍复杂一些的是利用百分比设置元素的大小来进行适配,或者利用 flex 等 css 去设置一些需要定制的宽度
- 再复杂一些的响应式页面,需要利用 CSS3 的 media query 属性来进行适配,大致思路是根据屏幕不同大小,来设置对应的 CSS 样式
上面的一些方法,其实也可以解决屏幕适配等问题
px2rem
如果利用 rem
来设置 CSS 的值,一般要通过一层计算才行,比如如果要设置一个长宽为 100px
的 div
,那么就需要计算出 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 的屏幕大小是 375px
,rem = 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
呢,这里一般分为两种办法
- 利用 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; }
}
- 利用 JS 来动态设置
document.getElementsByTagName('html')[0].style.fontSize = window.innerWidth / 10 + 'px';
由此可以通过设置不同的 html 基础值来达到在不同页面适配的目的,当然在使用 JS 设置时,需要绑定页面的 resize
事件来达到变化时更新 html 的 font-size
rem
适配进阶
一般我们获取到的视觉稿大部分是 iphone6 的,所以我们看到的尺寸一般是双倍大小的,在使用 rem
之前,我们一般会自觉的将标注 / 2,其实这也并无道理,但是当我们配合 rem
使用时,完全可以按照视觉稿上的尺寸来设置
- 设计给的稿子双倍的原因是 iphone6 这种屏幕属于高清屏,也即是设备像素比 dpr (device pixel ratio) 比较大,所以显示的像素较为清晰
- 一般手机的 dpr 是 1,iphone4,iphone5 这种高清屏是 2,iphone6s plus 这种高清屏是 3,可以通过 js 的
window.devicePixelRatio
获取到当前设备的 dpr - 拿到了 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');
- 设置完之后配合
rem
,修改:
@function px2rem($px) {
$rem: 75px;
@return ($px/$rem) + rem;
}
双倍 75,这样就可以完全按照视觉稿上的尺寸来了。不用在 / 2 了,这样做的好处是:
- 解决了图片高清问题
- 解决了 border 1px 问题(我们设置的 1px,在 iphone 上,由于 viewport 的 scale 是 0.5,所以就自然缩放成 0.5px)
但是 rem
也并不是万能的,下面也有一些场景是不适于使用 rem
的:
- 当用作图片或者一些不能缩放的展示时,必须要使用固定的 px 值,因为缩放可能会导致图片压缩变形等