Why
最近做了个移动app的宣传页,拿到设计稿的时候第一个反应就是必须要在所有的移动设备分辨率下都要保持一致(这不是废话嘛。。。),正好这阵子了解了一下动态rem方案,正好可以用在这个页面上,话不多少,先看效果
可以看到,最终效果就是在不同的分辨率下,展现的效果都是一致的,这里用的就是今天的主角--动态rem方案,其效果就是,一套css,多个分辨率适配,其实原理非常简单,接下啦就来介绍下如何实现。
How
首先来说下rem,rem单位是基于html的字体大小来进行计算的,比如屏幕的宽度是320像素,而里面某个容器的宽度为160像素,即屏幕宽度的一半。但是rem是基于html的字体的,乍一看这两组东西没有啥直接的联系,如果想实现自适应,必然就需要通过某种手段把rem基于的html字体大小和屏幕宽度联系起来,所以,需要先通过js获取屏幕宽度(这里我们就默认屏幕宽度等于窗口宽度),然后将获取到的宽度作为html的字体大小,再把需要设置宽度的容器设为基于这个宽度的rem值即可。先看下伪代码
//1. 获取屏幕宽度
var width = 调用屏幕宽度的api
//2. 将宽度设置为html的字体大小
html { font-size: width }
//3. 在css中给容器设置rem单位的值
.box { width: 0.5rem } //这里屏幕宽320px,容器宽160px,转换为rem计算160/320得到宽度为0.5rem
注意
上面提到的情况其实是最理想的情况,就是设计稿的宽度和屏幕宽度一样的情况下,是可以的,但是在实际开发中,总不可能针对所有分辨率的屏幕设计一套设计稿吧,所以这里又引入了一个关系,就是设计稿和实际宽度的缩放比例的关系,比如设计稿宽度为640像素,而调试设备的屏幕宽度为320像素,那么就存在一个320 /640=0.5的缩放比例,而在页面开发时,我们是通过设计稿来获取元素大小的,在页面上高度就需要去乘一下缩放比例,才能知道我们实际需要这个元素的大小,根据这个思路,再修改一下伪代码
//1. 获取屏幕宽度
var width = 调用屏幕宽度的api
//2. 获取缩放比例
var scale = width / 设计稿宽度
//3. 将宽度设置为html的字体大小
html { font-size: scale * 设计稿宽度}
//4. 在css中给容器设置rem单位的值
.box { width: 0.5rem } //这里设计稿宽320px,容器宽160px,转换为rem计算160/320得到宽度为0.5rem
仔细看一下不难发现,最后得到的html字体还是不变的,只是中间多了个看似无意义的scale,但这样写可以理解元素从设计稿到最终展示的转换过程,实际开发没有必要这样写。。。
可以看出,1rem的大小正好等于屏幕的宽度,但又发现了一个问题,比如某个文字字体大小为16px,1rem为320px,这样字体大小转换为rem后,得到的结果就是0.05rem,更甚者,某个元素的margin-right为3px,转换为rem就是0.009375rem,这样写会发现非常麻烦,小数点后面总是写好多,总结一下原因,就是因为rem的基数太大了,这里就可以考虑将rem的基数适当的缩小,比如,我们可以把html的字体大小设置为屏幕宽度的十分之一,这样10rem等于屏幕宽度,页面元素的计算都可以基于这个缩小后的值来计算。重新写下伪代码
//1. 获取屏幕宽度
var width = 调用屏幕宽度的api
//2. 将宽度设置为html的字体大小
html { font-size: width/10 }
//3. 在css中给容器设置rem单位的值
.box { width: 5rem } //这里屏幕宽320px,html字体大小为320/10=32,容器宽160px,转换为rem计算160/32得到宽度为5rem
这样修改了一下,是不是看起来更舒服了呢,现在就开始实战一波
do it
写一个小demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<script>
var width = document.documentElement.clientWidth;
var css = 'html{' +
'font-size:' + width / 10 + 'px' +
'}';
document.write('<style>' + css + '</style>');
</script>
<style>
* {
margin: 0;
padding: 0;
}
.box {
width: 2rem;
height: 2rem;
margin: 2rem auto;
background: #000;
border-radius: 50%;
}
</style>
<body>
<div class="box">
</div>
</body>
</html>
代码直接放到html文件中,点开运行,在不同的分辨率下,可以看到展示的大小比例都是一样的。
动态rem方案的核心大致就是这样,是不是很简单,接下来,需要自适应的页面开发时,只需要把px单位全部计算成rem即可
Ask a question
在实际开发中,我们会遇到一个问题,一个页面,动辄几十上百个元素,每个元素都有宽高,内外边距,字体大小,那么多px单位一个个算成rem,简直让人崩溃,10px就能表达的写法却可能改成0.33333333rem这种复杂的写法,是否有什么办法能够让我偷个懒,我只想在设计稿上量出来多大,就写多大。当然可以,这边就引入另外一个概念--css预处理工具,预处理工具能够帮我们简化计算,在生成最终的css之前先加工一下,最终得到想要的结果 它甚至引入了函数的概念,类似这样
function getRem(beforePX){
var resultRem = beforePX / psdWidth
return resultRem
}
css
.box {
width: getRem(100px)
}
stylus
这边我选择stylus作为示例,也可以选sass,语法可以参考官网
stylus工具的用法参照npm
- 全局安装stylus 命令行下运行
npm install -g stylus
- 安装完成后,在项目目录下新建一个test.styl文件,并把css样式用stylus的语法写在里面
将html中的style样式搬到styl文件中
index.styl
*
margin: 0
padding: 0
.box
width: 2rem
height: 2rem
margin: 2rem auto
background: green
border-radius: 50%
命令行工具执行以下命令
stylus -w index.styl -o index.css
这句命令的意思就是动态监听index.styl文件并在同级目录下生成index.css文件,然后命令行就可以最小化了(不要关闭!)
这时会发现同级目录下多了一个index.css文件,内容如下
* {
margin: 0;
padding: 0;
}
.box {
width: 2rem;
height: 2rem;
margin: 2rem auto;
background: #008000;
border-radius: 50%;
}
在styl文件中顶部定义rem计算函数
假设设计稿宽度640px,元素宽度为320px,页面字体大小基数为640/10=64px,那么320px最终转换为rem就是320/64=5rem,参考google,函数定义如下
rem(value) {
return unit(value/64, 'rem');
}
将计算的工作交给计算机
修改样式代码,所有需要计算rem的地方都换成这个函数的调用,参数就是我们在设计稿中实际量出来的大小
rem(value) {
return unit(value/64, 'rem');
}
*
margin: 0
padding: 0
.box
width: rem(128px)
height: rem(128px)
margin: rem(128px) auto
background: green
border-radius: 50%
保存修改,再来看看index.css
* {
margin: 0;
padding: 0;
}
.box {
width: 2rem;
height: 2rem;
margin: 2rem auto;
background: #008000;
border-radius: 50%;
}
可以看到,stylus编译后全部帮我们计算好了,来看看效果
至此,就通过stylus+rem实现了一个页面的动态rem方案,在pc上切换屏幕大小时需要手动刷新,这个方案更适合移动端,并且移动端不存在页面大小改变的情况,所以这边没有考虑,如果有强迫症的话,可以js监听一下window的resize时间,如有错误,欢迎指正拜拜!