js讓你的rem彈性佈局適配所有解析度(含豎屏適配)
<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;
#
# 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單位的元素:
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>