手淘rem的适配方案详解
什么是rem?
CSS3新增的一个相对单位rem(root em,根em)。rem是相对于根节点(或者是html节点)。如果根节点设置了font-size:10px;那么font-size:1.5rem;字体大小等于15px。
想想看,因为rem是一个相对单位,那么只要根节点的font-size属性随着屏幕自适应,那么下面通过rem设置的单位便可以达到自适应的目的!
当我们拿到设计稿的时候,主要会将视觉稿分成100份(主要为了以后能更好的兼容vh和vw),而每一份被称为一个单位a。同时1rem单位被认定为10a。就相当于1rem可以定为宽度为屏幕的10%。那么针对一个750px宽的设计稿,我们可以算出:
1a = 7.5px
1rem = 75px
那么这个设计稿也就被分成了10rem份。此时淘宝开源库lib-flexible
会为根节点的html和body元素设置一个data-dpr
和font-size
两个属性。其中dpr在IOS内会根据不同型号的机器动态的计算生成,大多数机型的data-dpr=2
但是对于安卓来说,由于其机型特别复杂多样,dpr会全部设置成1。font-size = 1rem
,对于一个750的设计稿来说,font-size=75px
,而这个就是rem的基准值。这样一来,对于视觉稿上的元素尺寸换算,只需要原始的px值除以rem基准值即可。例如此例视觉稿中的图片,其尺寸是176px * 176px,转换成为2.346667rem * 2.346667rem。
lib-flexible 原理
1. 计算页面的dpr
if (!dpr && !scale) {
var isAndroid = win.navigator.appVersion.match(/android/gi);
var isIPhone = win.navigator.appVersion.match(/iphone/gi);
var devicePixelRatio = win.devicePixelRatio;
if (isIPhone) {
// iOS下,3和3以上的屏,用3倍的方案,2用2倍方案,其余的用1倍方案
if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
dpr = 3;
} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
dpr = 2;
} else {
dpr = 1;
}
} else {
// 其他设备下,仍旧使用1倍的方案
dpr = 1;
}
scale = 1 / dpr;
}
2. 改写页面meta标签
flexible
实际上就是能过JS来动态改写meta
标签,代码类似这样:
var metaEl = doc.createElement('meta');
var scale = 1 / dpr;
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
if (docEl.firstElementChild) {
document.documentElement.firstElementChild.appendChild(metaEl);
} else {
var wrap = doc.createElement('div');
wrap.appendChild(metaEl);
documen.write(wrap.innerHTML);
}
如何转换rem
对于使用webpack的来说,其实也是有一个postcss插件的px2rem
,可以很轻松的配置相关属性,转换成相对于的rem单位。比如我们的.postcssrc
配置如下:
module.exports = {
"plugins": {
"postcss-px2rem": {
baseDpr: 1,
remUnit: 37.5,
onePxComment: '1px',
forcePxComment: '!px',
keepComment: '!no',
forcePxProperty: ['font-size'] // font-size强制使用px单位
}
}
}
1. 文本字号不建议使用rem
前面大家都见证了如何使用rem来完成H5适配。那么文本又将如何处理适配。是不是也通过rem来做自动适配。
显然,我们在iPhone3G和iPhone4的Retina屏下面,希望看到的文本字号是相同的。也就是说,我们不希望文本在Retina屏幕下变小,另外,我们希望在大屏手机上看到更多文本,以及,现在绝大多数的字体文件都自带一些点阵尺寸,通常是16px和24px,所以我们不希望出现13px和15px这样的奇葩尺寸。
如此一来,就决定了在制作H5的页面中,rem并不适合用到段落文本上。所以在Flexible整个适配方案中,考虑文本还是使用px作为单位。只不过使用[data-dpr]
属性来区分不同dpr下的文本字号大小。
配置完成之后,在实际使用时,你只要像下面这样使用:
.selector {
width: 150px;
height: 150px;
font-size: 12px;
border: 1px solid #ddd;
}
px2rem处理之后将会变成:
.selector {
width: 2rem;
height: 2rem;
border: 1px solid #ddd;
}
[data-dpr="1"] .selector {
font-size: 12px;
}
[data-dpr="2"] .selector {
font-size: 28px;
}
[data-dpr="3"] .selector {
font-size: 42px;
}
因为不同的dpr,屏幕会缩成不同比例,所以这里的字号会根据dpr进行相应的放大。举个例子,如果data-dpr = 2
那么页面的viewport会被缩小,所以字号需要相应的乘以比例进行放大。