Rem 布局

在閱讀本文之前,先需要了解 設(shè)備像素眶熬、設(shè)備獨(dú)立像素妹笆、CSS-像素块请、桌面瀏覽器上的-viewport移動瀏覽器上的-viewport拳缠。

rem 是 css 的長度單位墩新,它是相對于 <html> 元素的 font-size 的相對值。假設(shè) html { font-size: 20px; }窟坐,那么 1rem 就等于 20px海渊。

根據(jù) layout viewport 來設(shè)置 rem

可以根據(jù) layout viewport 的大小,來設(shè)置<html> 元素的 font-size 的值哲鸳。

function () {
  const setRem = () => document.documentElement.style.fontSize = document.documentElement.clientWidth / 10
  window.addEventListener('DOMContentLoaded', setRem, false)
  window.addEventListener('orientationchange', setRem, false)
} ()

上面的腳本根據(jù)手機(jī)瀏覽器的 layout viewport 來設(shè)置 <html> 元素的 font-size 臣疑。 因此在任何手機(jī)上,1rem 都是 1/10 layout viewport徙菠。由于我們一般都會加上 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"> 標(biāo)簽讯沈,所以 layout viewport 和 visual viewport 是相等的,都代表手機(jī)屏幕能夠顯示的 css 像素婿奔,因此在任何手機(jī)上缺狠,1rem 都是 1/10 手機(jī)屏幕的寬度,10rem 都是占滿手機(jī)屏幕的寬度萍摊。

當(dāng)網(wǎng)頁需要在移動端顯示的時候挤茄,我們給元素設(shè)置 css 尺寸的時候,都要使用 rem冰木,不要使用 px穷劈,因?yàn)槭褂?px 的元素在不同的手機(jī)上顯示效果差別太大。舉個例子片酝,某個元素的 width 為 320px囚衔,那么該元素在 iphone5 上滿屏顯示,在 iphone6 上則不會滿屏顯示雕沿,右側(cè)會留有 55px 的空白练湿。如果某個元素的 width 為 10rem,則無論在 iphone5 還是 iphone6 上审轮,都是滿屏顯示肥哎。

如果給我們一個適配 iphone6 的網(wǎng)頁設(shè)計稿,該設(shè)計稿的尺寸應(yīng)該為 750 物理像素疾渣,因?yàn)?iphone6 的設(shè)備物理分辨率為 750篡诽。iphone6 的 devicePixelRatio (以下簡稱 dpr) 為 2,也就是用 2 個物理像素來顯示 1 個設(shè)備獨(dú)立像素榴捡。在不縮放網(wǎng)頁的情況下杈女,1 個 css 像素由 1 個設(shè)備獨(dú)立像素來渲染,也就是由 2 個物理像素渲染。因此 750 物理像素轉(zhuǎn)換為 css 像素應(yīng)該是 375px达椰。如果我們在適配 iphone6 的設(shè)計稿上測量某個元素的尺寸為 64 物理像素翰蠢,那么該元素的 css 像素應(yīng)該是 32px。根據(jù)上面腳本的設(shè)置啰劲,iphone6 上 1rem 相當(dāng)于 37.5px梁沧,所以 32px 就相當(dāng)于 32 / 37.5 rem。為了避免每次計算元素的 rem 尺寸都需要用 css 像素除以 37.5蝇裤,我們可以寫成 sass 的函數(shù) (該 sass 函數(shù)只適用于設(shè)計稿是 iphone6 的情況)廷支。

@function px2rem($px) {
  @return $px / 37.5 + rem;
}

假設(shè)我們的網(wǎng)頁設(shè)計稿的尺寸為 750 像素,那么該設(shè)計稿就是針對 iphone6 的設(shè)計稿栓辜。在設(shè)計稿上測量某個元素為 100 像素恋拍,則只需要指定 width: px2rem(50)。之所以參數(shù)是 50 不是 100藕甩,是因?yàn)?100 是物理像素芝囤,iphone6 上 100 物理像素就相當(dāng)于 50px。如果覺得麻煩辛萍,可以將 iphone6 的設(shè)計稿縮放成 375 像素,這樣再在設(shè)計稿上測量出某個元素為 50 像素羡藐,就是 50px贩毕,不需要除以 2 了。

根據(jù) devicePixelRatio 來設(shè)置 rem

根據(jù) layout viewport 的大小來設(shè)置 rem仆嗦,可以保證只要使用 rem 設(shè)置元素的尺寸辉阶,同一個元素在不同的手機(jī)上所占屏幕的百分比都是相同的,這就相當(dāng)于元素采用百分比來布局瘩扼。在大屏幕的手機(jī)上谆甜,該元素所占的物理尺寸就大,在小屏幕的手機(jī)上集绰,該元素所占的物理尺寸就小规辱。但是有些情況下,我們希望某個元素在不同手機(jī)上栽燕,所占的物理尺寸都差不多罕袋,而不考慮該元素所占屏幕的百分比是多少 (比如我們設(shè)置字體大小,希望字體在不同設(shè)備上的物理尺寸都差不多)碍岔。這種情況下浴讯,根據(jù) layout viewport 來設(shè)置 rem 就不適合了。

我們可以采用一種新的方案來代替 根據(jù) layout viewport 來設(shè)置 rem 的方案蔼啦,那就是 根據(jù)設(shè)備的 devicePixelRatio 來設(shè)置 rem 1rem = 50px * dpr榆纽。dpr 為 1 的設(shè)備上,1rem = 50px,dpr 為 2 的設(shè)備上奈籽,1rem = 100px饥侵。同時還會根據(jù) devicePixelRatio 設(shè)置 <meta name="viewport"> 標(biāo)簽的 initial-scale 的值 initial-scale = 1 / dpr。dpr 為 1 的設(shè)備上唠摹,initial-scale = 1爆捞,dpr 為 2 的設(shè)備上,initial-scale = 0.5勾拉。這樣可以保證煮甥,在不同的設(shè)備上,相同 rem 的元素所占的物理尺寸差不多藕赞。

我們通過在 <head> 標(biāo)簽內(nèi)加上下面的壓縮的 js 代碼成肘,可以實(shí)現(xiàn)根據(jù) devicePixelRatio 來設(shè)置 rem。另外我們不要自己定義 <meta name="viewport"> 標(biāo)簽斧蜕,因?yàn)橄旅娴拇a會自動定義該標(biāo)簽双霍,并且根據(jù) devicePixelRatio 來設(shè)置 initial-scale 的屬性值。

<script>!function(e){function t(a){if(i[a])return i[a].exports;var n=i[a]={exports:{},id:a,loaded:!1};return e[a].call(n.exports,n,n.exports,t),n.loaded=!0,n.exports}var i={};return t.m=e,t.c=i,t.p="",t(0)}([function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=window;t["default"]=i.flex=function(normal,e,t){var a=e||100,n=t||1,r=i.document,o=navigator.userAgent,d=o.match(/Android[\S\s]+AppleWebkit\/(\d{3})/i),l=o.match(/U3\/((\d+|\.){5,})/i),c=l&&parseInt(l[1].split(".").join(""),10)>=80,p=navigator.appVersion.match(/(iphone|ipad|ipod)/gi),s=i.devicePixelRatio||1;p||d&&d[1]>534||c||(s=1);var u=normal?1:1/s,m=r.querySelector('meta[name="viewport"]');m||(m=r.createElement("meta"),m.setAttribute("name","viewport"),r.head.appendChild(m)),m.setAttribute("content","width=device-width,user-scalable=no,initial-scale="+u+",maximum-scale="+u+",minimum-scale="+u),r.documentElement.style.fontSize=normal?"50px": a/2*s*n+"px"},e.exports=t["default"]}]);  flex(false,100, 1);</script>

下面是這段代碼的源碼批销。

'use strict';

/**
 * @param {Boolean} [normal = false] - 默認(rèn)開啟頁面壓縮以使頁面高清;  
 * @param {Number} [baseFontSize = 100] - 基礎(chǔ)fontSize, 默認(rèn)100px;
 * @param {Number} [fontscale = 1] - 有的業(yè)務(wù)希望能放大一定比例的字體;
 */
const win = window;
export default win.flex = (normal, baseFontSize, fontscale) => {
  const _baseFontSize = baseFontSize || 100;
  const _fontscale = fontscale || 1;

  const doc = win.document;
  const ua = navigator.userAgent;
  const matches = ua.match(/Android[\S\s]+AppleWebkit\/(\d{3})/i);
  const UCversion = ua.match(/U3\/((\d+|\.){5,})/i);
  const isUCHd = UCversion && parseInt(UCversion[1].split('.').join(''), 10) >= 80;
  const isIos = navigator.appVersion.match(/(iphone|ipad|ipod)/gi);
  let dpr = win.devicePixelRatio || 1;
  if (!isIos && !(matches && matches[1] > 534) && !isUCHd) {
    // 如果非iOS, 非Android4.3以上, 非UC內(nèi)核, 就不執(zhí)行高清, dpr設(shè)為1;
    dpr = 1;
  }
  const scale = normal ? 1 : 1 / dpr;

  let metaEl = doc.querySelector('meta[name="viewport"]');
  if (!metaEl) {
    metaEl = doc.createElement('meta');
    metaEl.setAttribute('name', 'viewport');
    doc.head.appendChild(metaEl);
  }
  metaEl.setAttribute('content', `width=device-width,user-scalable=no,initial-scale=${scale},maximum-scale=${scale},minimum-scale=${scale}`);
  doc.documentElement.style.fontSize = normal ? '50px' : `${_baseFontSize / 2 * dpr * _fontscale}px`;
};

根據(jù) devicePixelRatio 來設(shè)置 rem洒闸,在 iphone6 上,dpr = 2均芽,1rem = 100px丘逸,initial-scale=0.5,layout viewport = 750px掀宋,因此如果在適配 iphone6 的 750 像素的設(shè)計稿上測量一個元素的尺寸為 100 像素深纲,則可以直接轉(zhuǎn)換為 1rem,測量某個元素為 12px劲妙,則轉(zhuǎn)換為 0.12rem湃鹊,這樣我們可以很方便的將測量的像素轉(zhuǎn)換為 rem。

總結(jié)

盡量使用根據(jù) devicePixelRatio 來設(shè)置 rem镣奋,對于那些固定大小的元素 (字體币呵、底部導(dǎo)航欄圖標(biāo)等),設(shè)置 rem 尺寸侨颈,對于可變大小的元素 (布局元素)富雅,使用百分比尺寸,或者使用 flex 布局肛搬。

參考

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末没佑,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子温赔,更是在濱河造成了極大的恐慌蛤奢,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,729評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異啤贩,居然都是意外死亡待秃,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評論 3 399
  • 文/潘曉璐 我一進(jìn)店門痹屹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來章郁,“玉大人,你說我怎么就攤上這事志衍∨” “怎么了?”我有些...
    開封第一講書人閱讀 169,461評論 0 362
  • 文/不壞的土叔 我叫張陵楼肪,是天一觀的道長培廓。 經(jīng)常有香客問我,道長春叫,這世上最難降的妖魔是什么肩钠? 我笑而不...
    開封第一講書人閱讀 60,135評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮暂殖,結(jié)果婚禮上价匠,老公的妹妹穿的比我還像新娘。我一直安慰自己呛每,他們只是感情好霞怀,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著莉给,像睡著了一般。 火紅的嫁衣襯著肌膚如雪廉沮。 梳的紋絲不亂的頭發(fā)上颓遏,一...
    開封第一講書人閱讀 52,736評論 1 312
  • 那天,我揣著相機(jī)與錄音滞时,去河邊找鬼叁幢。 笑死,一個胖子當(dāng)著我的面吹牛坪稽,可吹牛的內(nèi)容都是我干的曼玩。 我是一名探鬼主播,決...
    沈念sama閱讀 41,179評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼窒百,長吁一口氣:“原來是場噩夢啊……” “哼黍判!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起篙梢,我...
    開封第一講書人閱讀 40,124評論 0 277
  • 序言:老撾萬榮一對情侶失蹤顷帖,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贬墩,經(jīng)...
    沈念sama閱讀 46,657評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡榴嗅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了陶舞。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嗽测。...
    茶點(diǎn)故事閱讀 40,872評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖肿孵,靈堂內(nèi)的尸體忽然破棺而出唠粥,到底是詐尸還是另有隱情,我是刑警寧澤颁井,帶...
    沈念sama閱讀 36,533評論 5 351
  • 正文 年R本政府宣布厅贪,位于F島的核電站,受9級特大地震影響雅宾,放射性物質(zhì)發(fā)生泄漏养涮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評論 3 336
  • 文/蒙蒙 一眉抬、第九天 我趴在偏房一處隱蔽的房頂上張望贯吓。 院中可真熱鬧,春花似錦蜀变、人聲如沸悄谐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽爬舰。三九已至,卻和暖如春寒瓦,著一層夾襖步出監(jiān)牢的瞬間情屹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評論 1 274
  • 我被黑心中介騙來泰國打工杂腰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留垃你,地道東北人。 一個月前我還...
    沈念sama閱讀 49,304評論 3 379
  • 正文 我出身青樓喂很,卻偏偏與公主長得像惜颇,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子少辣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評論 2 361

推薦閱讀更多精彩內(nèi)容