怎麼通俗易懂理解rem佈局
rem佈局是現在比較流行的移動端佈局,掌握了這種佈局方式後,可以很輕易的實現佈局自適應手機螢幕。目前淘寶首頁就是採用的這種佈局方式。
我曾經上網查過很多資料,看過很多解釋原理的和使用方法的,可是還是一臉懵逼,可能我是一枚女程式設計師,腦子轉不過來,必須要弄得很清楚透徹才能想明白這個佈局的原理。後來我自己在紙上琢磨,用數學公式成功把自己弄明白了。希望也能幫到大家。就像一道數學題一樣,見下題:
首先,假設UI給了一張 a(px)的設計稿,手機螢幕寬度為b(px),設定html根元素的字型大小為c(px)。如果現在設計稿上面有個d(px)的元素,那麼這個元素在這個手機螢幕上佔多少(px)?那麼應該設定多少rem呢?
解:設這個元素在手機螢幕上佔e(px)
(1)那麼元素在設計稿上面的比例和元素在手機螢幕上的比列保持一致,得:d/a=e/b => e=db/a (px)
(2)由於手機上html根元素字型為c(px),所以:1(rem)=c(px)。(這是基礎知識,不知道的可以自行百度w3c標準)
(3)那麼e(px)=e/c(rem)
(4)把“(1)"得到的e帶入”(3)“得:e(px)=db/ac(rem)
(5)這時候,令c=b/a,則e(px)=db/ac(rem)=d(rem)
此時便可得出:當html的font-size為 b/a (px)的時候,設計稿上的元素為d(px),則在css裡面設定為d(rem)
(6)但是由於b/a很可能是小數或者很小的整數,而瀏覽器頁面可設定的最小字型大小為12px,所以可以設定html的font-size為(b/a)*10或者(b/a)*100更合理準確。這裡以(b/a)*100為例,如果html的font-size為(b/a)*100,即:c=(b/a)*100,重新帶入”(5)“計算得:e(px)=db/ac(rem)=d/100(rem)。
(7)這樣就得出結論了:當html字型元素設定為(b/a)*100px時,則在移動端裝置上,根據設計稿可以把設計稿上的元素長度除以100便為需要設定的rem。
舉例:設計稿為200px,手機螢幕寬為400px,則設定html字型元素為(400/200)*100畫素,即:200(px),若設計稿上某個元素寬為96px,則在css裡面可以設定為0.96rem,實現了同比縮放或擴大
重點:在搞明白了這個原理後,主要就是怎麼設定html的字型大小了。並且是動態設定,(b/a)*100,設計稿固定,則a為固定已知的,主要就是怎麼在不同裝置上動態獲取b(手機屏寬),這裡有個js,可以動態計算並設定html的字型,在進入頁面的時候載入該js即可(此外如果不用這個js設定,也可以用媒體查詢方式設定不用屏寬情況下html的font-size):
js設定方式程式碼(以設計稿寬為375px舉例):
//designWidth:設計稿的實際寬度值,需要根據實際設定
(function(designWidth) {
var doc = document,
win = window,
docEl = doc.documentElement,
remStyle = document.createElement("style"),
tid;
function refreshRem() {
var width = docEl.getBoundingClientRect().width;
var rem = width * 100 / designWidth;
remStyle.innerHTML = 'html{font-size:' + rem + 'px;}';
}
if (docEl.firstElementChild) {
docEl.firstElementChild.appendChild(remStyle);
} else {
var wrap = doc.createElement("div");
wrap.appendChild(remStyle);
doc.write(wrap.innerHTML);
wrap = null;
}
//要等 wiewport 設定好後才能執行 refreshRem,不然 refreshRem 會執行2次;
refreshRem();
win.addEventListener("resize", function() {
clearTimeout(tid); //防止執行兩次
tid = setTimeout(refreshRem, 300);
}, false);
win.addEventListener("pageshow", function(e) {
if (e.persisted) { // 瀏覽器後退的時候重新計算
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}
}, false);
if (doc.readyState === "complete") {
doc.body.style.fontSize = "16px";
} else {
doc.addEventListener("DOMContentLoaded", function(e) {
doc.body.style.fontSize = "16px";
}, false);
}
})(375);