Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CSS零碎之em、rem #4

Open
kekobin opened this issue Jul 1, 2019 · 0 comments
Open

CSS零碎之em、rem #4

kekobin opened this issue Jul 1, 2019 · 0 comments

Comments

@kekobin
Copy link
Owner

kekobin commented Jul 1, 2019

移动端的开发基本很少直接使用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像素和物理像素的关系一般是1:2或者1:3,即1个css像素容纳2到3个物理像素,实现高清的效果。

一般移动端页面的开发流程是:设计人员以某个机型作为标准,设计好UI。前端开发针对这个机型的UI做开发,然后其它机型相对的去等比例缩放。
这里以iphone6作为标准(因为实际开发中基本也是用它做设计),它的物理像素为750x1334,css像素为375*667,假设UI上图片a的宽度为140,那么如何把它转换成以rem为单位的值呢?
现在屏幕宽度是已知的10rem,要求UI上宽140的rem值,假设为X,由下图可以很容易的得到比例关系:

屏幕宽度/UI宽度 = x/140 = 10rem / 750
=》
x = 140 / 750 * 10 rem

image
一般可以在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呢?记住如下原则即可:

  1. 如果属性值根据元素的font-size获得,则使用em,如padding、margin、line-height等
  2. 其他情况都使用rem

参考文章

CSS像素、物理像素、逻辑像素、设备像素比、PPI、Viewport

@kekobin kekobin changed the title CSS零碎 之em、rem CSS零碎之em、rem Jul 1, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant