移动端的开发基本很少直接使用 px 作为单位了,目前最常用的是 rem。不过在这之前其实还有个 em 单位,和 rem 长得非常的像,那么它们有什么区别呢?又有什么不一样的适用场景呢?
注意:无论使用 em,还是 rem,客户端最终解析的值依旧是 px!
em:相对父级元素字体大小的倍数
从 title 的解释就可以看出,em 的基准是其父级元素,不过这个父级元素要求是设置有 font-size 值的,如下面的例子:
<div class="father" style="font-size:20px;">
<div class="son" style="font-size:2em;"></div>
</div>
那么 son 的字体大小就是 2 * 20px = 40px,此时,如果 father 的字体大小变化了,那么 son 的也会跟着变化。假如没有父元素,则基准就是 body(由于默认浏览器默认字体为 16px,所以默认情况 1em=16px)。
rem:相对 html 根元素字体大小的倍数
跟上面解释 body 基准时的差不多,默认情况下 1rem=16px。此时,只要根元素字体大小不变,那么相对于它的 rem 就不会变。
对于移动端的各种机型来讲,由于不同的机型屏幕尺寸、分辨率都不一样,不太可能使用相同的根元素字体大小作为基准,所以所谓 rem 布局,就是通过 js 动态计算出不同机型的根元素字体大小值,来对页面进行等比例的缩放,达到适配大部分机型的效果。
那么具体如何去设定这个基准呢?
假设把手机屏幕宽度均分成 10 等份(因为 rem 布局就是针对宽度去做设定的),规定其中的一份作为根元素的 font-size 值,那么根元素 font-size 值就可由下述公式获得:
document.documentElement.style.fontSize = document.documentElement.clientWidth / 10 + 'px';
在这个基准下,那么 1rem 的值也随之得到了:
1rem = document.documentElementstyle..fontSize = document.documentElement.clientWidth / 10 + 'px';
那么,不同屏幕的机型由于屏幕宽度不同,也就得到了在 ” 屏幕宽度均分成 10 等份 ” 这个标准下的 rem 相对值。比较完整的计算方式如下:
// 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()
}
})
注意:这里的标准 “10” 可以是任一值,只要跟下面转换公式中用到的标准值保持一致即可,这里只是为了计算方便才这么设定。
这样,rem 的基础生态就搭建好了。那么如何基于这套标准应用到开发中去呢?
在继续之前,先来了解下 ” 像素 ” 这个知识点,因为接下来都会用到它的概念。
像素分为两种:设备像素和 CSS 像素
1、设备像素(device independent pixels): 设备屏幕的物理像素,任何设备的物理像素的数量都是固定的
2、CSS 像素(CSS pixels): 又称为逻辑像素,是为 web 开发者创造的,在 CSS 和 javascript 中使用的一个抽象的层
在 pc 端,css 像素和物理像素是 1:1 的关系;而在移动端,由于类似 retina 的各种高清屏的出现,css 像素和物理像素的关系一般是 2:1 或者 3:1,即 1 个物理像素容纳 2 到 3 个 css 像素,实现高清的效果。
一般移动端页面的开发流程是:设计人员以某个机型作为标准,设计好 UI。前端开发针对这个机型的 UI 做开发,然后其它机型相对的去等比例缩放。
这里以 iphone6 作为标准 (因为实际开发中基本也是用它做设计),它的物理像素为 750×1334,css 像素为 375*667,假设 UI 上图片 a 的宽度为 140,那么如何把它转换成以 rem 为单位的值呢?
现在屏幕宽度是已知的 10rem,要求 UI 上宽 140 的 rem 值,假设为 X,由下图可以很容易的得到比例关系:
屏幕宽度 /UI 宽度 = x/140 = 10rem / 750
=》x = 140 / 750 * 10 rem
一般可以在 sass 中通过封装预处理函数进行这个转换过程:
$UI_WIDTH: 750;
@function px2rem($px) {@return ${ $px / $UI_WIDTH * 10}rem;
//=>or @return ${$px / 75}rem;
}
img{width: px2rem(140);
}
这就是 ”rem 布局 ” 原理的整个实现过程!
而随着社区各种工具的完善,现在也无需手动去使用如 px2rem 的这种预处理函数去转换,比较流行的做法是使用 postcss 的 postcss-px2rem 插件去自动处理,开发时仍然按照 px 的方式去编程,postcss 配置例:
postcss: function() {return [px2rem({remUnit: 75})]; // 设置基准值,75 是以 iphone6 的标准
}
这里的 remUnit 设置是有一定规则的,比如屏幕宽度等分成 10 份,当 UI 以 iphone6(即物理像素宽度 750)设计时,remUnit=75;当 UI 以 iphone5(即物理像素宽度 640)设计时,remUnit=64。可以看出它中遵循如下公式:
remUnit = 物理像素宽度 / 设定的屏幕宽度等分值;
至于具体的 js 等分逻辑封装可以参看手淘的 flexible。
em 还是 rem?
那么在实际开发中,究竟适用 em 还是 rem 呢?记住如下原则即可:
- 如果属性值根据元素的 font-size 获得,则使用 em,如 padding、margin、line-height 等
- 其他情况都使用 rem
本文收录在个人的 Github 上 https://github.com/kekobin/bl… , 觉得有帮助的,欢迎 start 哈!