<html>
<head>
<meta charset="utf-8" />
<title>自动计算字体</title>
<meta name="description" content="px转rem" />
<meta name="author" content="xiaoweili" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<style>
@media only screen and (max-width: 1080px),only screen and (max-width:1080px) {
html,body {
font-size:33.75px
}
}
@media only screen and (max-width: 960px),only screen and (max-width:960px) {
html,body {
font-size:30px
}
}
@media only screen and (max-width: 720px),only screen and (max-width:720px) {
html,body {
font-size:22.5px
}
}
@media only screen and (max-device-width: 640px),only screen and (max-width:640px) {
html,body {
font-size:20px
}
}
@media only screen and (max-device-width: 540px),only screen and (max-width:540px) {
html,body {
font-size:16.875px
}
}
@media only screen and (max-device-width: 480px),only screen and (max-width:480px) {
html,body {
font-size:15px
}
}
@media only screen and (max-width: 400px) {
html,body {
font-size:12.5px
}
}
@media only screen and (max-width: 360px),only screen and (max-width:320px) {
html,body {
font-size:11.25px
}
}
@media only screen and (max-width: 320px),only screen and (max-device-width:320px) {
html,body {
font-size:10px
}
}
</style>
<style type="text/css">
*{padding: 0;margin: 0}
.demo{
width: 10rem;
height: 10rem;
font-size: 1rem;
background: #ff0000;
}
</style>
<script type="text/javascript">
console.time("test");
/*
# 按照宽高比例设定html字体, width=device-width initial-scale=1版
# @pargam win 窗口window对象
# @pargam option{
designWidth: 设计稿宽度,必须
designHeight: 设计稿高度,不传的话则比例按照宽度来计算,可选
designFontSize: 设计稿宽高下用于计算的字体大小,默认20,可选
callback: 字体计算之后的回调函数,可选
}
# return Boolean;
# xiaoweili@tencent.com
# ps:请尽量第一时间运行此js计算字体
*/
!function(win, option) {
var count = 0,
designWidth = option.designWidth,
designHeight = option.designHeight || 0,
designFontSize = option.designFontSize || 20,
callback = option.callback || null,
root = document.documentElement,
body = document.body,
rootWidth, newSize, t, self;
//返回root元素字体计算结果
function _getNewFontSize() {
var scale = designHeight !== 0 ? Math.min(win.innerWidth / designWidth, win.innerHeight / designHeight) : win.innerWidth / designWidth;
return parseInt( scale * 10000 * designFontSize ) / 10000;
}
!function () {
rootWidth = root.getBoundingClientRect().width;
self = self ? self : arguments.callee;
//如果此时屏幕宽度不准确,就尝试再次获取分辨率,只尝试20次,否则使用win.innerWidth计算
if( rootWidth !== win.innerWidth && count < 20 ) {
win.setTimeout(function () {
count++;
self();
}, 0);
} else {
newSize = _getNewFontSize();
//如果css已经兼容当前分辨率就不管了
if( newSize + 'px' !== getComputedStyle(root)['font-size'] ) {
root.style.fontSize = newSize + "px";
return callback && callback(newSize);
};
};
}();
//横竖屏切换的时候改变fontSize,根据需要选择使用
win.addEventListener("onorientationchange" in window ? "orientationchange" : "resize", function() {
clearTimeout(t);
t = setTimeout(function () {
self();
}, 300);
}, false);
}(window, {
designWidth: 640,
designHeight: 1136,
designFontSize: 20,
callback: function (argument) {
console.timeEnd("test")
}
});
</script>
</head>
<body>
<div class="warp">
<div class="demo">
<p>width:10rem</p>
<p>height:10rem</p>
</div>
<div style="width:100%;font-size:14px;">
<pre>
</pre>
</div>
/*改变html的font-size可以等比改变所有用了rem单位的元素:demo的标准像素值除以html的像素值*/
1. 这段代码对viewport有要求,必须是width=device-width initial-scale=1,即窗口的大小是设备物理宽度(分辨率 / devicePixelRatio),并且禁止缩放。另外还有一种做法就是手机淘宝的做法,窗口大小是分辨率宽度,然后缩放倍数是1/devicePixelRatio,这里暂且不讨论。
2.就是解决安卓上的问题。经过实测,有些安卓机器,使用1的viewport,在页面刚加载的时候。不管是读取window.innerWidth,还是doc的getBoundingClientRect().width,或者是body的clientWidth,都不是设备的物理宽度。所以只好祭出黑魔法setTimeout,一试果然可以,异步100ms执行获取屏幕宽度的代码就准确了。但是这种不可控的代码让人不爽。
因为width=device-width initial-scale=1,documentElement的宽度又是100%,所以当这两个值相等的时候我们可以认为目前获取到的屏幕宽度是准确的。那么使用此条件作为判断条件,不断的setTimeout(fun(){}, 0)去判断,当此条件为真时改变documentElement的字体。可以尽可能快的执行目标代码。但是又万一这两个值一直不相等又不能无限的死循环下去,所以设置了一个尝试上限,到上限之后用窗口宽度来计算(缩放比例不对的话用户起码可以看到完整的页面)。在chrome下测试,执行40次代码的平均时间是230ms,考虑到安卓机的js引擎速度,将上限设为了20。
3.是执行时机,个人建议将这段代码放到head里,第一时间计算好html的fontSize,避免重绘。如果你有有一些跟获取dom元素尺寸相关的操作,就得放到这个计算函数的回调里面了,这时候就不能放到head里(因为运行的时候dom都还没加载),只能放到底部或者doc的ready事件里了。最佳实践是有一个全屏的loading画面,当fontSize计算好了之后再把真正的页面展示出来。
</body>
</html>