不要再用js设置rem了,教你使用em方式实现自适应

20,439 阅读7分钟

在做移动端适配的时候,很多人第一反应就是使用 rem ,通过动态设置 html 上的 font-size 来进行页面的自适应,基本原理就是 rem 表示的是 root em ,页面中所有的值都是基于html上的 font-size ,相对的进行对应的变化

尤其是智能手机出现之后,我们没有办法在去固定我们的设备宽高,需要考虑 响应式 设计,根据浏览器窗口大小有不同的布局方式

css 中的单位

绝对单位

单位分为绝对单位和相对单位,其中绝对单位有 px、mm、cm、in、pt、pc 这些都可以相互转换

1in = 25.4mm = 2.54cm = 6pc = 72pt = 96px

当然我们还需要知道的是,css 虽然是一个绝对单位,但是它并不等于显示器的像素,需要通过 dpr 进行换算

最常见的相对单位是 emrem

em

1em 表示的是当前元素的字号,可以看到 1em 是 16px,因为当前元素的 font-size 是 16px,浏览器会根据相对单位 em 计算出绝对单位 px,所以当你改变了这个元素的 font-size,其对应的 padding 也会随之变化,设置 padding、height、width、border-radius 这些属性的时候,使用 em 很方便,会动态根据 font-size 变化

那既然元素的 em 是根据当前元素的 font-size 来的,那给当前元素设置 font-size 使用 em ,那对应的值是什么呢?

可以看到,目前 font-size 变成了 32px ,为了得到 font-size 的值,需要参考继承的字号。这个时候如果设置了 padding 的值也是 2em ,虽然 font-size 和 padding 都是 2em ,但是它们的值是不一样的,padding 的大小为 64px ,font-size 先取到,然后根据 font-size 计算出padding

这里增加了计算的复杂性,所以一般font-size我们给固定的值就好,否则就会多层嵌套导致最终的结果不符合预期,所以如果不小心使用 em ,会让 em 很难按照我我们预想的来

所以我们有更好用的 rem

rem

在 html 文档中,根节点是所有其它元素的祖先,:root 表示根节点的伪类选择器,可以用来选中 html ,html 类型选择器和 :root 伪类选择器优先级是有区别的

rem 是 root em 的缩写,rem 不是相对于当前元素,而是相对于根元素,所以,不论什么位置,使用 rem 单位都是相对于根元素的 font-size

实际使用的选择性

既然 rem 这么好用,并且不存在 em 那么复杂的计算逻辑,是不是在项目中我们都用 rem 就好了呢?当然实际在项目中想必大家都是 rem 梭哈,这里我总结了一些适用场景

rem 一般情况下就是用来设置 font-size ,px 设置边框,em 设置绝大部分属性,比如padding margin border-radio 等等,这样统一的字号标准,让页面不论是缩放还是适配都游刃有余,所以当你拿捏不准使用什么方式来设置一些元素的时候,就按照上述的来,一般是没有什么问题的

使用 js 设置根元素 rem

自从有了 rem 这个便捷的相对单位,我们就有了一些奇怪的操作,比如用 js 设置根元素大小这个操作,就是将网页的根元素字号根据屏幕的大小动态设置为一个固定值,然后在页面中根据 ui 给出的图,换算成 rem 值,进行各种适配

甚至衍生出了一些 px 转换成 rem 的插件,当然这些做确实有些方便,但是不可否认的是它也有一定的问题

  1. 当屏幕小的时候,font-size 的大小会变成 10px ,但是我们好多系统最小字也就是 12px ,10px 展示有问题,导致我们需要给所有的元素上设置 至少为 1.2rem 才能保证显示正常
  2. 当屏幕过大的时候,比如移动端转到 pc 端,页面的根元素 font-size 又会变的很大,感官上根本不能用

实际应用

用以上所学,我们创建一个标题带段落的说明

<style>
  :root{
    font-size: 0.875em;
  }
  .container{
    padding: 1em;
    border-radius: .5em;
    border: 1px solid #000;
  }
  h1{
    margin-top: 0;
    font-size: 1rem;
    font-weight: bold;
  }
  p{
    font-size: .8rem;
  }
</style>

<body>
  <div class="container">
    <h1>FE情报局</h1>
    <p>哈喽大家好,这里是FE情报局,我是局长,今天这篇文章深入理解的话,会学到如何使用现代 css 的布局方案,为我们提供一个响应式的布局,能够让我们不论是在页面缩放,还是不同的屏幕之间,都有良好的用户体验,当然根元素默认字号 14px</p>
  </div>
</body>

这确实是会增加我们一些工作量,因为你需要思考什么时候使用 em ,什么时候使用 rem 以及 px 之间相互切换,但是好处也是很明显的

如果你想要将当前的内容做一个响应式

只需要这样

@media (min-width: 800px){
  :root{
    font-size: 1em
  }
}

@media (min-width: 1200px){
  :root{
    font-size: 1.2em
  }
}

随着屏幕的变化,字号逐渐增加,即便是对一个组件进行不同的自适应,你只需要更改对应组件的 font-size 即可

当然 css 中相对单位还有常见的内容

视口相对单位

  • vh: 视口高度的1/100
  • vw: 视口宽度的1/100
  • vmin: 视口宽度或者高度中较小的一方1/100
  • vmax: 视口宽度或者高度中较大的一方1/100

50vh 也就是视口高度的一半

刚才我们使用媒体查询定义了根元素 font-size ,当页面宽度变化到指定像素的时候会突然变成我们设置的内容,现在既然有了 vw ,是不是可以使用 vw 进行设置,视口改变时,元素自然过渡

实践一下

:root{
  font-size: 2vw
}

这样在小屏幕上因为有最小字号限制,所以能够展示最小 12px 的字,但是屏幕一旦变大,导致字号也跟着变大,变小虽然字能看,但是边距会随之减小到很小的程度

有没有什么办法呢?

calc

calc 大家基本都用过,它可以对两个以及以上的值进行基本运算,比如calc(1em + 10px),支持加减乘除

对于根节点,为了保证最小值,我们可以这样

:root{
  font-size: calc(0.5em + 1vw)
}

这样能保证最小值,也不至于屏幕大了字号过大,做了一个较好的适配

总结

这就是使用现代 css 的是配置方式

  1. 更多的使用相对单位来设置一些属性
  2. rem 设置字号,em 设置额外内容,px 设置边框
  3. 使用 calc+vw 也能做好一般自适应

既然有很多人讨论这个问题,我觉得我有必要补充一下

第一点:对于 rem 来说,全局使用当前看是没有什么问题,但是大家可以思考一下,使用了 rem,本质就是你的网站在不同的屏幕上缩放,在移动端还好,如果一旦放到 pc 端,那效果大家想必都很清楚,那么大的屏幕看到的内容有可能还没有移动端的多,这纯粹是一种偷懒的方式,只是把移动端放大而已

第二点:通篇 rem,根节点的 font-size 是变化的,之后页面中所有字体大小的部分都不能继承默认值,需要手动全部改成想要的字体大小

第三点:三方组件,比如富文本编辑器,写出来的内容是px的,和 rem 的内容格格不入,除非你用一些特殊的方式将其隔离(比如iframe)

第四点:文章的重点还是希望能够配合 em、vw、px、calc 等方式去处理一些逻辑,元素的字体大小根据 rem 设置,这样动态改变 rem 的大小,就能够让 em 动态适应

第五点:现代布局通过 flex + vw 等方式已经足够解决大部分问题了,虽然有一些计算成分,不同的设备下,我想看到的是更多的内容而不是更大的字

说到底还是需要掌握 flex、grid 等现代布局的方式