首先了解一下rem是什么?它的全称是 font size of the root element (根元素的字体大小,它是CSS3中新增加的一个尺寸(度量)单位,根节点(html)的font-size决定了rem的尺寸,也就是说它是一个相对单位,相对于(html)。
rem 和 px 直接的转换公式可以写为:1rem = 1 * htmlFontSize
例如 html { font-szie: 100px }
则 1rem = 100px, html { font-szie: 50px }
则 1rem = 50px
移动端适配原理都是采用等比缩放的方式 —— 获得目标屏幕宽度和设计稿宽度的比,作为 rem 的基值(缩放系数),设置为html标签的字体大小。
我们一般会以设计稿的宽度为基准宽度,在这个宽度时设置 1rem=100px (优点不言而喻,前端开发者在切图、重构页面的时候,通过直接位移小数点的方式,就可以将UI图中测量到的 px 值换算成对应的 rem 值,方便快捷),我们把rem/px比率设为定值,即rem2px = 100。接下来我们算一下基准宽度下根元素的字体应该怎么设置。
我们先定义几个变量:系统默认字体大小--defaultFontSize; 设计稿宽度--designWidth;根元素html字体大小--htmlFontSize。
1rem = htmlFontSize * px
htmlFontSize = rem / px 即 htmlFontSize = rem2px
html{
font-size: rem2px * px;
}
我们知道,font-size可以用百分比表示,即htmlFontSize相对于defaultFontSize的百分比
(htmlFontSize / defaultFontSize) * 100% = (rem2px / defaultFontSize) * 100%
html{
font-size: (rem2px / defaultFontSize) * 100%;
}
现在我们就可以以设计稿1px = 0.01rem来还原设计稿了,但是这只是适应设备屏幕和设计稿宽度一致的情况下,如果屏幕的宽度不等于设计稿宽度的时候,我们要怎么设置字体来适配呢?我们要明白不论设备宽度怎么变化,宽度和字体的比例大小应该是恒定不变的,这就是我们要做的适配,即:
designWidth / ((rem2px / defaultFontSize) * 100%) = window.innerWidth / currentFontSize
我们就可以这样来做适配:
const designWidth = 750; // 基准宽度 这个值可以改变,该值为设计稿的宽度
const rem2px = 100;
const htmlNode = document.getElementsByTagName('html')[0];
const defaultFontSize = parseFloat(window.getComputedStyle(htmlNode, null).getPropertyValue('font-size'));
document.documentElement.style.fontSize = `${(window.innerWidth / designWidth) * (rem2px / defaultFontSize) * 100}%`
Window.getComputedStyle()方法返回一个对象,该对象在应用活动样式表并解析这些值可能包含的任何基本计算后报告元素的所有CSS属性的值。关于 getComputedStyle 的兼容性问题,在 Chrome 和 Firefox 是支持该属性的,同时 IE 9 10 11 也是支持相同的特性的,IE 8并不支持这个特性。 IE 8 支持的是 element.currentStyle 这个属性,这个属性返回的值和 getComputedStyle 的返回基本一致
window.getComputedStyle(htmlNode, null)的打印结果:
image.png
细心地同学会发现,这样计算出来的值其实是有小的偏差,查资料发现html的font-size使用的是: getPropertyValue('font-size') 而 1rem 使用的是 getPropertyValue('width'),偏差出在计算 font-size 的时候浏览器进行了四舍五入。我们需要优化defaultFontSize的计算:
const designWidth = 750;
const rem2px = 100;
const d = window.document.createElement('div');
d.style.width = '1rem';
d.style.display = "none";
const head = window.document.getElementsByTagName('head')[0];
head.appendChild(d);
const defaultFontSize = parseFloat(window.getComputedStyle(d, null).getPropertyValue('width'));
d.remove();
document.documentElement.style.fontSize = `${(window.innerWidth / designWidth) * (rem2px / defaultFontSize) * 100}%`
到此为止已经可以完美的做到了移动端适配。
对了,别忘了在 head 中设置:<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0",user-scalable=no />
如果对屏幕旋转也许要做处理的话,可优化如下:
var d = window.document.createElement('div');
d.style.width = '1rem';
d.style.display = "none";
var head = window.document.getElementsByTagName('head')[0];
head.appendChild(d);
var defaultFontSize = parseFloat(window.getComputedStyle(d, null).getPropertyValue('width'));
d.remove();
document.documentElement.style.fontSize = window.innerWidth / designWidth * rem2px / defaultFontSize * 100 + '%';
var st = document.createElement('style');
var portrait = "@media screen and (min-width: "+window.innerWidth+"px) {html{font-size:"+ ((window.innerWidth/(designWidth/rem2px)/defaultFontSize)*100) +"%;}}";
var landscape = "@media screen and (min-width: "+window.innerHeight+"px) {html{font-size:"+ ((window.innerHeight/(designWidth/rem2px)/defaultFontSize)*100) +"%;}}"
st.innerHTML = portrait + landscape;
head.appendChild(st);
return defaultFontSize
网友评论