前言
一般而言,我们用CSS设置字体大小和元素长宽是这样的:
.name {
font-size: 16px;
width: 100px;
}
(由于字体大小和元素长宽的原理一样,下面统一讨论字体。)
本来一切好好的,但到了不同的屏幕上效果差别就很大了。假设你的设计稿是按照iphone6的尺寸来标注,那在iphone6 plus上,由于你的字体还是一样大,所以在iphone6 plus上看起来会小一点。如果屏幕尺寸再大,则会再小,效果和UI设计的看起来就不太一样了。特别是某些固定尺寸的元素,看起来就会很奇怪。
原始的做法
更好的设计是,在iphone6 plus上把iphone6的设计放大。
比如6的屏幕宽度是375,字体大小为16,而6p的宽度为414,那字体就应该是414/375*16=17.6px。也就是根据两者之间的比例来放大字体。然而,屏幕并不只有6和6p,也许还要适配其他不同尺寸的屏幕。而且如果每种适配都需要重新修改字体样式的话,工作量就太大了。
当然,最简单的做法就是在head里面设置initial-scale,根据不同屏幕来决定缩放的值。但是,这种做法有个不好的地方,就是它本身是一个放大功能,字体和图片被放大之后会变模糊,对于追求比较高的前端页面来说可能难以接受。
还有另一个方面,IE并不能缩放px字体的大小。如果在IE上进行了缩放,那字体还是那么大。
三种不同的单位
接下来介绍三种不同的长度单位(CSS Units)。
px
px,就是pixels。翻译为像素并不十分精确,可能翻译为点更好,但是已经有另一个单位pt(points),所以,也只能继续翻译为像素了。这里的px不同于一般的像素,它的特性就是在不同的设备上代表的大小不同。在低清屏上,1px就代表1像素,而在高清屏上,比如iphone 6上则代表2个像素,而在iphone6 plus上则代表3个像素,因为6p是3倍的高清屏。所以,当设计稿给出iphone 6的尺寸时,你必须把它除以2,写成px,才能在移动设备上正确地显示尺寸。
em
Relative to the font-size of the element (2em means 2 times the size of the current font)
意思就是,某个元素的字体大小与它的父元素的相对单位。
这个很好理解,比如父元素A,拥有子元素B。假设A字体设置为1em,B设置为2em,而1em=16px,那最后A的字体是16px,B的字体是32px。而B又拥有子元素C,C设置为0.5em,则C的字体为32px*0.5=16px。这里的32px是B元素的字体大小。
rem
Relative to font-size of the root element
和em类似,不同的是,rem相对的是根元素的字体大小。
假设html的字体为16px,拥有A元素,字体为2rem,那A的字体就是32px。假设A拥有B,而B为2rem,那B的字体也为32px,因为它相对的是html,而不是A。假设B的字体为2em,那B就是64px了。
再来看看rem的兼容性,也是相当不错的。
利弊
使用px的话,基本上页面元素的字体大小都是固定的,甚至修改起来也很麻烦。而用em就能解决适配的问题,但坏处是每个大小都是相对父元素的,一旦某个节点有所变动,很容易造成其他节点也要变动,而且本身不是特别直观,单看某个节点是1em并不能得到它的具体大小。而rem基本是最优方案了,既可以很好地适配,也可以直观地修改。
下面会介绍将rem方案应用到项目里的方法。
REM方案
用px写CSS,构建时替换为rem
并不提倡直接在代码里写rem,因为你并不知道你当前的1rem代表多少。所以最好的方式是代码里直接用px描述字体和大小,并在后期将其转化为rem。
适配不同屏幕的方案
针对6和6p这些不同的屏幕,我们可以使用media query来定义root element的字体大小,这样就能轻松做到根据不同屏幕展现同样的视觉效果。
构建方案
构建方案很简单,分为两步,一个针对.css文件,另一个针对html,包括html中的style标签以及html中的inline-style。
处理普通的css文件
目前比较好用的处理css文件的插件是gulp-postcss和postcss-pxtorem配合使用,比如像这样:
var postcss = require('gulp-postcss');
var pxtorem = require('postcss-pxtorem');
var options = {
rootValue: 10,
propWhiteList: [],
minPixelValue: 1};
gulp.src('www/*.css').pipe(postcss([pxtorem(options)])).pipe(gulp.dest('build/'));
postcss-pxtorem提供了不同的参数设置来转化css中的px。比如rootValue用来定义转化时根元素的值,mediaQuery决定是否转换media query中的大小,minPixelValue用来定义最小的不需转化的px值(比如可以不转化1px的大小)。如果想要特制某些元素的大小不被转化,可以通过8PX这样的大写方式来解决,因为pxtorem不会转化这部分css,而浏览器却能够识别。此外还有白名单、黑名单、小数点位数、是否替换原来的px等参数可供设置。
处理html中的css
这部分比较有意思。微信提供了posthtml-px2rem的方案来解决inline-style的问题,但不处理html中的style标签,因为他们已经把css独立出去解决。但是,不少框架还会在文件中使用style标签,如果只需要处理inline-style的话也可以用这个方案。
更通用一点的处理方式是gulp-posthtml、posthtml-postcss、postcss-pxtorem,流程基本就是处理html中的css中的px,这里会统一把inline-style一起解决,所以是个不错的选择。
var posthtml = require('gulp-posthtml');
var posthtmlcss = require('posthtml-postcss');
var pxtorem = require('postcss-pxtorem');
var options = {};
gulp.src('www/*.html').pipe(posthtml([posthtmlcss([pxtorem(options)])])).pipe(gulp.dest('build/'));
这里使用了和上面同样的postcss-pxtorem,参数option也是一样的。
完结
我们在代码中使用px并以统一的规范来实现界面,根据不同的屏幕定制不同的基础字体大小,并在构建时将px转为rem让其适配不同的屏幕。
其实一般我们都需要额外定制html标签的字体大小,不让其转换,这样会更显得直观一点。
参考
postcss-pxtorem
posthtml-px2rem
gulp-posthtml
posthtml-postcss
gulp-postcss
微信:REM 解决方案
css3的字体大小单位[rem]到底好在哪?
web app变革之rem