1. 程式人生 > >怎麼通俗易懂理解rem佈局

怎麼通俗易懂理解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);