什么是rem
首先来了解一下什么是em:
作为font-size的单位时,其代表父元素的字体大小,em作为其他属性单位时,代表自身字体大小。
s1、s2、s5、s6的font-size和line-height分别是多少px?
<div class="p1">
<div class="s1">1</div>
<div class="s2">1</div>
</div>
<div class="p2">
<div class="s5">1</div>
<div class="s6">1</div>
</div>
.p1 {font-size: 16px; line-height: 32px;}
.s1 {font-size: 2em;}
.s2 {font-size: 2em; line-height: 2em;}
.p2 {font-size: 16px; line-height: 2;}
.s5 {font-size: 2em;}
.s6 {font-size: 2em; line-height: 2em;}
比起到处写死的px值,em似乎更有张力,改动父元素的字体大小,子元素会等比例变化。
有些人提出用em来做弹性布局页面,但其复杂的计算让人诟病,甚至有人专门做了个px和em的计算器,不同节点像素值对应的em值。
em做弹性布局的缺点还在于牵一发而动全身,一旦某个节点的字体大小发生变化,那么其后代元素都得重新计算。
rem:
作用于非根元素时,相对于根元素字体大小;rem作用于根元素字体大小时,相对于其出初始字体大小
/* 作用于根元素,相对于原始大小(16px),所以html的font-size为32px*/
html {font-size: 2rem}
/* 作用于非根元素,相对于根元素字体大小,所以为64px */
p {font-size: 2rem}
rem有rem的优点,em有em的优点,em就是为字体和行高而生的,有些时候子元素字体就应该相对于父元素,元素行高就应该相对于字体大小;而rem的有点在于统一的参考系。
Rem布局原理
rem布局的本质是什么?这是我问过很多人的一个问题,但得到的回答都差强人意。
其实rem布局的本质是等比缩放,一般是基于宽度,试想一下如果UE图能够等比缩放,那该多么美好啊
假设我们将屏幕宽度平均分成100份,每一份的宽度用x表示,x = 屏幕宽度 / 100,如果将x作为单位,x前面的数值就代表屏幕宽度的百分比
p {width: 50x} /* 屏幕宽度的50% */
如果要页面元素随着屏幕宽度等比缩放,我们需要上面的x单位,css中没有这个单位,但是通过rem这个桥梁可以实现x。
/* 子元素设置rem,通过改变html元素的字体大小,可以使子元素的实际大小发生改变*/
html{font-size: 16px}
p{width: 2rem} /* 32px */
html {font-size: 32px}
p {width: 2rem} /* 64px */
如果让html元素字体大小,恒等于1/100,那1rem和1x就等价了
/*
width / 100 就是让屏幕分成100等份,1份在屏幕宽度里是多少。
*/
html{font-size: width / 100} /* 子元素1rem就是屏幕宽度的1% */
/*
50rem = 50x = 屏幕宽度的50% rem就相当于上面的x单位 1rem就是1%
*/
p{width: 50rem}
通过js设置,可以让font-size一直恒等于1/100,一般需要在页面dom ready、resize和屏幕旋转中设置。
document.documentElement.style.fontSize = document.documentElement.clientWidth / 100 + 'px';
将UE图中元素像素单位值转换为rem值:
元素宽度 / UE图宽度 *100
假设UE图尺寸是640px,UE图中的一个元素宽度是100px,根据公式100/640 *100 = 15.625rem。
100/640就知道100宽度占UE图宽度的百分之几 0.15625在乘以100就是15.625%就是15.625rem(rem就相当于是百分比单位)
p{width: 15.625rem}
/*
100px的元素占屏幕宽度的15.625%,因为ui的尺寸是以iphone5(高清屏)
为基准并且使用的是物理像素,而我们使用的是css像素,dpr为2,
占满屏幕实际只要设置320px个css像素就行了,
因此这里设置50px其实就是100个物理像素,因此这里应该还要除以2才对
15.625 / 2;
*/
看到这里,实际上你也可以想到用百分数来代替rem,你可以直接设置元素15.625%也可以,每次都通过公式计算百分比,然后直接将视口设置为设备宽度这样html元素会等于设备宽度即可。
<meta name="viewport" content="width=device-width, initial-scale=1">
比rem更好的方案:
css引入vw、vh
vw —— 视口宽度的 1/100;vh —— 视口高度的 1/100
可以看出vw就是上面所述的x,1vw就是屏幕的1%,有了vw可以不用计算font-size值
下面的方案是等价的,可以看到vw比rem更简单,毕竟rem是为了实现vw么
/* rem方案 */
html {fons-size: width / 100}
p {width: 15.625rem}
/* vw方案 */
p {width: 15.625vw}
/* vw还可以和rem方案结合,这样计算html字体大小就不需要用js了 */
html {fons-size: 1vw} /* 1vw = width / 100 */
p {width: 15.625rem}
vw的缺点:
首先是兼容性问题:
另外,在使用弹性布局时,一般会限制最大宽度,比如在pc端查看我们的页面,此时vw就无法力不从心了,因为除了width有max-width,其他单位都没有,而rem可以通过控制html根元素的font-size最大值,而轻松解决这个问题
Rem不是银弹
rem是弹性布局的一种实现方式,弹性布局可以算作响应式布局一种,但响应式布局不是弹性布局弹性布局强调等比缩放,100%还原,响应式布局强调不同屏幕有不同的显示,比如媒体查询。
rem适用的地方:
一般内容型的网站不适合用rem,因为大屏用户可以选择自己选择是需要更大的字体,还是更多的内容,一旦使用了rem,就剥夺了用户的自由,比如百度知道,百度经验都没有使用rem布局;
一些偏向app类的,图标类的,图片类的,比如淘宝,活动页面,比较适合使用rem,因为调大字体时并不能调大图标的大小;
rem存在的问题:
首先是字体问题,字体大小并不能使用rem,字体的大小和字体宽度,并不成线性关系,这个函数我还没算出来,所以字体大小不能使用rem。
由于设置了根元素字体的大小,会影响所有没有设置字体大小的元素,因为字体大小是会继承的,难道要每个元素都显示设置字体大小???
解决办法:
可以再body上做字体修正,将body设置为16px,但用户自己设置的字体大小会失效,可以再body上做字体修正,将body设置为16px,但用户自己设置的字体大小会失效。
html{font-size: width / 100}
body{font-size: 16px}
实现响应式字体:
可以通过修改body的字体大小来实现,同时所有设置字体大小的地方都用em。
@media screen and (max-width:320px){
body{font-size:16px}
}
@media screen and (min-width:481px) and (max-width:640px){
body{font-size:18px}
}
@media screen and (min-width: 641px){
body{font-size:20px}
}
p{font-size:1.2em}
p,a{font-size:1.2em}
第二,如果用户在PC端浏览,页面过宽怎么办?一般我们会设置一个最大的宽度,大于这个宽度的话页面居中,两边留白。
var clientWidth = document.documentElement.clientWidth;
clientWidth = clienWidth < 780 ? clientWidth : 780;
document.documentElement.style.fontSize = clientWidth / 100 + 'px';
第三,如果用户禁用了js怎么办?其实这种用户真不多了,要不放弃吧。。。
首先可以添加noscript标签提示用户:
<noscript>开启JavaScript,获得更好的体验</noscript>
给html添加一个320时的默认字体大小,保证页面可以显示
html {fons-size: 3.2px}
如果你想要更好的体验,不如添加媒体查询吧
@media screen and (min-width: 320px) {
html {font-size: 3.2px}
}
@media screen and (min-width: 481px) and (max-width:640px) {
html {font-size: 4.8px}
}
@media screen and (min-width: 641px) {
html {font-size: 6.4px}
}
rem不是银弹,这个世上也没有银弹,每个方案都有其优点,也有其缺点,学会做出选择和妥协,rem仅能做到内容的缩放,但是对于非矢量资源,比如图片放大时的失真,并无法解决。
Rem布局方案
通过上面可以得出实现缩放布局,共有四种方案
rem+media-query rem+js rem+vw vw
但是上面的方案还有个问题,就是分成100份的话,假设屏幕宽度320,此时html大小是3.2px,但浏览器支持最小字体大小是12px,怎么办?那就分成10份呗,只要把上面的100都换成10就好了。
下面给一个rem+js方案的完整例子,css的计算没有使用预处理器,这个很简单
html代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>rem布局——rem+js</title>
</head>
<body>
<noscript>开启JavaScript,获得更好的体验</noscript>
<div class="p1">
宽度为屏幕宽度的50%,字体大小1.2em
<div class="s1">
字体大小1.2.em
</div>
</div>
<div class="p2">
宽度为屏幕宽度的40%,字体大小默认
<div class="s2">
字体大小1.2em
</div>
</div>
</body>
</html>
css代码如下:
html {
font-size: 32px; /* 320/10 */
}
body {
font-size: 16px; /* 修正字体大小 */
/* 防止页面过宽 */
margin: auto;
padding: 0;
width: 10rem;
/* 防止页面过宽 */
outline: 1px dashed green;
}
/* js被禁止的回退方案 */
@media screen and (min-width: 320px) {
html {font-size: 32px}
body {font-size: 16px;}
}
@media screen and (min-width: 481px) and (max-width:640px) {
html {font-size: 48px}
body {font-size: 18px;}
}
@media screen and (min-width: 641px) {
html {font-size: 64px}
body {font-size: 20px;}
}
noscript {
display: block;
border: 1px solid #d6e9c6;
padding: 3px 5px;
background: #dff0d8;
color: #3c763d;
}
/* js被禁止的回退方案 */
.p1, .p2 {
border: 1px solid red;
margin: 10px 0;
}
.p1 {
width: 5rem;
height: 5rem;
font-size: 1.2em; /* 字体使用em */
}
.s1 {
font-size: 1.2em; /* 字体使用em */
}
.p2 {
width: 4rem;
height: 4rem;
}
.s2 {
font-size: 1.2em /* 字体使用em */
}
js代码如下:
var documentElement = document.documentElement;
function callback() {
var clientWidth = documentElement.clientWidth;
// 屏幕宽度大于780,不在放大
clientWidth = clientWidth < 780 ? clientWidth : 780;
documentElement.style.fontSize = clientWidth / 10 + 'px';
}
document.addEventListener('DOMContentLoaded', callback);
window.addEventListener('orientationchange' in window ? 'orientationchange' : 'resize', callback);
总结:
移动端rem布局原理就是
将屏幕分成100等份,让元素随着屏幕的变化而等比缩放。以某个设
备为标准,计算元素占据屏幕百分之几。
比如ui以iphone6为标准,设计图是375(如果设计图是750,最后计算的结果还要除以2)的屏幕宽度,那么375的1%就是375 / 100 = 3.75 将其设置为根元素的font-size值,子元素设置尺寸为rem,这样子元素1rem就是屏幕的1%,如何转换为rem,因为1rem就是屏幕的1%,所以我们就要计算元素的宽度占用屏幕宽度的百分之几。因此100 / 375 = 26%.666; 转换为rem就是26.666%。
根据上面可以得出公式:
document.documentElement.fontSize = "width / 100"; /* 相对于屏幕的1% */
元素rem = 元素的宽度 / ui图宽度 * 100 /* 计算元素占用屏幕的百分之几 */
转载于: https://yanhaijing.com/css/2017/09/29/principle-of-rem-layout/ 颜海镜博客