Rem布局的原理解析
面試中發現很多人對rem布局的原理不是很清楚,只停留在會使用的階段,或者理解完全是錯誤的,本文將給大家講清楚rem布局的原理,使用方案等知識
什麼是Rem
rem和em很容易混淆,其實兩個都是css的單位,並且也都是相對單位,現有的em,css3才引入的rem,在介紹rem之前,我們先來了解下em
em作為font-size的單位時,其代表父元素的字體大小,em作為其他屬性單位時,代表自身字體大小——MDN
我在面試時經常問會一道和em有關的題,來看一下面試者對css細節的了解程度,如下,問s1、s2、s5、s6的font-size和line-height分別是多少px,先來想一想,結尾處有答案和解釋
<div class="p1">nt<div class="s1">1</div>n t<div class="s2">1</div>n</div>n<div class="p2">nt<div class="s5">1</div>n t<div class="s6">1</div>n</div>n
.p1 {font-size: 16px; line-height: 32px;}n.s1 {font-size: 2em;}n.s2 {font-size: 2em; line-height: 2em;}nn.p2 {font-size: 16px; line-height: 2;}n.s5 {font-size: 2em;}n.s6 {font-size: 2em; line-height: 2em;}n
em可以讓我們的頁面更靈活,更健壯,比起到處寫死的px值,em似乎更有張力,改動父元素的字體大小,子元素會等比例變化,這一變化似乎預示了無限可能
有些人提出用em來做彈性布局頁面,但其複雜的計算讓人詬病,甚至有人專門做了個px和em的計算器,不同節點像素值對應的em值,o(╯□╰)o
em做彈性布局的缺點還在於牽一髮而動全身,一旦某個節點的字體大小發生變化,那麼其後代元素都得重新計算,X﹏X
rem作用於非根元素時,相對於根元素字體大小;rem作用於根元素字體大小時,相對於其出初始字體大小——MDN
rem取值分為兩種情況,設置在根元素時和非根元素時,舉個例子
/* 作用於根元素,相對於原始大小(16px),所以html的font-size為32px*/nhtml {font-size: 2rem}nn/* 作用於非根元素,相對於根元素字體大小,所以為64px */np {font-size: 2rem}n
rem有rem的優點,em有em的優點,尺有所短,寸有所長,我一直不覺得技術沒有什麼對錯,只有適合不適合,有對錯的是使用技術的人,傑出與優秀的區別就在於能否選擇合適的技術,並讓其發揮優勢
我一直覺得em就是為字體和行高而生的,有些時候子元素字體就應該相對於父元素,元素行高就應該相對於字體大小;而rem的有點在於統一的參考系
Rem布局原理
rem布局的本質是什麼?這是我問過很多人的一個問題,但得到的回答都差強人意。
其實rem布局的本質是等比縮放,一般是基於寬度,試想一下如果UE圖能夠等比縮放,那該多麼美好啊
假設我們將屏幕寬度平均分成100份,每一份的寬度用x表示,x = 屏幕寬度 / 100,如果將x作為單位,x前面的數值就代表屏幕寬度的百分比
p {width: 50x} /* 屏幕寬度的50% */ n
如果想要頁面元素隨著屏幕寬度等比變化,我們需要上面的x單位,不幸的是css中並沒有這樣的單位,幸運的是在css中有rem,通過rem這個橋樑,可以實現神奇的x
通過上面對rem的介紹,可以發現,如果子元素設置rem單位的屬性,通過更改html元素的字體大小,就可以讓子元素實際大小發生變化
html {font-size: 16px}np {width: 2rem} /* 32px*/nnhtml {font-size: 32px}np {width: 2rem} /*64px*/n
如果讓html元素字體的大小,恆等於屏幕寬度的1/100,那1rem和1x就等價了
html {fons-size: width / 100}np {width: 50rem} /* 50rem = 50x = 屏幕寬度的50% */ n
如何讓html字體大小一直等於屏幕寬度的百分之一呢? 可以通過js來設置,一般需要在頁面dom ready、resize和屏幕旋轉中設置
document.documentElement.style.fontSize = document.documentElement.clientWidth / 100 + px; n
那麼如何把UE圖中的獲取的像素單位的值,轉換為已rem為單位的值呢?公式是元素寬度 / UE圖寬度 * 100,讓我們舉個例子,假設UE圖尺寸是640px,UE圖中的一個元素寬度是100px,根據公式100/640*100 = 15.625
p {width: 15.625rem}n
下面來驗證下上面的計算是否正確,下面的表格是UE圖等比縮放下,元素的寬度
| UE圖寬度 | UE圖中元素寬度 |
| ----- | -------- |
| 640px | 100px |
| 480px | 75px |
| 320px | 50px |
下面的表格是通過我們的元素在不同屏幕寬度下的計算值
| 頁面寬度 | html字體大小 | p元素寬度 |
| ----- | --------------- | ---------------- |
| 640px | 640/100 = 6.4px | 15.625*6.4=100px |
| 480px | 480/100=4.8px | 15.625*4.8=75px |
| 320px | 320/100=3.2px | 15.625*3.2=50px |
可以發現,UE圖寬度和屏幕寬度相同時,兩邊得出的元素寬度是一致的
上面的計算過程有些繁瑣,可以通過預處理的function來簡化過程,下面是sass的例子,less類似
$ue-width: 640; /* ue圖的寬度 */nn@function px2rem($px) {n @return #{$px/$ue-width*100}rem;n}nnp {n width: px2rem(100);n}n
上面的代碼編譯完的結果如下
p {width: 15.625rem} n
其實有了postcss後,這個過程應該放到postcss中,源代碼如下
p {width: 100px2rem} n
postcss會對px2rem這個單位進行處理,處理後的結果如下,感興趣的話來寫一個px2rem的postcss插件吧
p {width: 15.625rem} n
比Rem更好的方案
上面提到想讓頁面元素隨著頁面寬度變化,需要一個新的單位x,x等於屏幕寬度的百分之一,css3帶來了rem的同時,也帶來了vw和vh
vw —— 視口寬度的 1/100;vh —— 視口高度的 1/100 —— MDN
聰明的你也許一經發現,這不就是單位x嗎,沒錯根據定義可以發現1vw=1x,有了vw我們完全可以繞過rem這個中介了,下面兩種方案是等價的,可以看到vw比rem更簡單,畢竟rem是為了實現vw么
/* rem方案 */nhtml {fons-size: width / 100}np {width: 15.625rem}nn/* vw方案 */np {width: 15.625vw}n
vw還可以和rem方案結合,這樣計算html字體大小就不需要用js了
html {fons-size: 1vw} /* 1vw = width / 100 */np {width: 15.625rem}n
雖然vw各種優點,但是vw也有缺點,首先vw的兼容性不如rem好,使用之前要看下
| 兼容性 | Ios | 安卓 |
| ---- | ---- | ---- |
| rem | 4.1+ | 2.1+ |
| vw | 6.1+ | 4.4+ |
另外,在使用彈性布局時,一般會限制最大寬度,比如在pc端查看我們的頁面,此時vw就無法力不從心了,因為除了width有max-width,其他單位都沒有,而rem可以通過控制html根元素的font-size最大值,而輕鬆解決這個問題
Rem不是銀彈
rem是彈性布局的一種實現方式,彈性布局可以算作響應式布局的一種,但響應式布局不是彈性布局,彈性布局強調等比縮放,100%還原;響應式布局強調不同屏幕要有不同的顯示,比如媒體查詢
用戶選擇大屏幕有兩個幾個出發點,有些人想要更大的字體,更大的圖片,比如老花眼的我;有些人想要更多的內容,並不想要更大的圖標;有些人想要個鏡子。。。——顏海鏡
我認為一般內容型的網站,都不太適合使用rem,因為大屏用戶可以自己選擇是要更大字體,還是要更多內容,一旦使用了rem,就剝奪了用戶的自由,比如百度知道,百度經驗都沒有使用rem布局;一些偏向app類的,圖標類的,圖片類的,比如淘寶,活動頁面,比較適合使用rem,因為調大字體時並不能調大圖標的大小
rem可以做到100%的還原度,但同事rem的製作成本也更大,同時使用rem還有一些問題,下面我們一一列舉下:
首先是字體的問題,字體大小並不能使用rem,字體的大小和字體寬度,並不成線性關係,所以字體大小不能使用rem;由於設置了根元素字體的大小,會影響所有沒有設置字體大小的元素,因為字體大小是會繼承的,難道要每個元素都顯示設置字體大小???
我們可以在body上做字體修正,比如把body字體大小設置為16px,但如果用戶自己設置了更大的字體,此時用戶的設置將失效,比如合理的方式是,將其設置為用戶的默認字體大小
html {fons-size: width / 100}nbody {font-size: 16px} n
那字體的大小如何實現響應式呢?可以通過修改body字體的大小來實現,同時所有設置字體大小的地方都是用em單位,對就是em,因為只有em才能實現,同步變化,我早就說過em就是為字體而生的
@media screen and (min-width: 320px) {ntbody {font-size: 16px}n}n@media screen and (min-width: 481px) and (max-width:640px) {ntbody {font-size: 18px}n}n@media screen and (min-width: 641px) {ntbody {font-size: 20px}n}nnp {font-size: 1.2em}np a {font-size: 1.2em}n
第二,如果用戶在PC端瀏覽,頁面過寬怎麼辦?一般我們都會設置一個最大寬度,大於這個寬度的話頁面居中,兩邊留白
var clientWidth = document.documentElement.clientWidth;nclientWidth = clientWidth < 780 ? clientWidth : 780;ndocument.documentElement.style.fontSize = clientWidth / 100 + px;n
設置body的寬度為100rem,並水平居中
body { margin: auto; width: 100rem } n
第三,如果用戶禁用了js怎麼破?其實這種用戶真不多了,要不放棄吧。。。
首先可以添加noscript標籤提示用戶
<noscript>開啟JavaScript,獲得更好的體驗</noscript> n
給html添加一個320時的默認字體大小,保證頁面可以顯示
html {fons-size: 3.2px} n
如果你想要更好的體驗,不如添加媒體查詢吧
@media screen and (min-width: 320px) {nthtml {font-size: 3.2px}n}n@media screen and (min-width: 481px) and (max-width:640px) {nthtml {font-size: 4.8px}n}n@media screen and (min-width: 641px) {nthtml {font-size: 6.4px}n}n
rem不是銀彈,這個世上也沒有銀彈,每個方案都有其優點,也有其缺點,學會做出選擇和妥協
Rem布局方案
通過上面可以得出最好的彈性布局方案是,rem+js方案,同時還要解決noscript問題,解決字體問題,解決屏幕過寬問題
但是上面的方案還有個問題,就是分成100份的話,假設屏幕寬度320,此時html大小是3.2px,但瀏覽器支持最小字體大小是12px,怎麼辦?那就分成10份唄,只要把上面的100都換成10就好了
下面給一個完整的例子,css的計算沒有使用預處理器,這個很簡單
html代碼如下
<!DOCTYPE html>n<html lang="en">n<head>n <meta charset="UTF-8">n <meta name="viewport" content="width_=device-width, initial-scale=1, maximum-scale=1">nn <title>rem布局</title>n</head>n<body>n <noscript>開啟JavaScript,獲得更好的體驗</noscript>nn <div class="p1">n 寬度為屏幕寬度的50%,字體大小1.2emn <div class="s1">n 字體大小1.2.emn </div>n </div>nn <div class="p2">n 寬度為屏幕寬度的40%,字體大小默認n <div class="s2">n 字體大小1.2emn </div>n </div>n</body>n</html>n
css代碼如下
html {n font-size: 32px; /* 320/10 */n}nbody {n font-size: 16px; /* 修正字體大小 */n /* 防止頁面過寬 */n margin: auto;n padding: 0;n width: 10rem;n /* 防止頁面過寬 */n outline: 1px dashed green;n}nn/* js被禁止的回退方案 */n@media screen and (min-width: 320px) {n html {font-size: 32px}n body {font-size: 16px;}n}n@media screen and (min-width: 481px) and (max-width:640px) {n html {font-size: 48px}n body {font-size: 18px;}n}n@media screen and (min-width: 641px) {n html {font-size: 64px}n body {font-size: 20px;}n}nnnoscript {n display: block;n border: 1px solid #d6e9c6;n padding: 3px 5px;n background: #dff0d8;n color: #3c763d;n}n/* js被禁止的回退方案 */nn.p1, .p2 {n border: 1px solid red;n margin: 10px 0;n}nn.p1 {n width: 5rem;n height: 5rem;nn font-size: 1.2em; /* 字體使用em */n}nn.s1 {n font-size: 1.2em; /* 字體使用em */n}nn.p2 {n width: 4rem;n height: 4rem;n}n.s2 {n font-size: 1.2em /* 字體使用em */n}n
js代碼如下
var documentElement = document.documentElement;nnfunction callback() {n var clientWidth = documentElement.clientWidth;n // 屏幕寬度大於780,不在放大n clientWidth = clientWidth < 780 ? clientWidth : 780;n documentElement.style.fontSize = clientWidth / 10 + px;n}nndocument.addEventListener(DOMContentLoaded, callback);nwindow.addEventListener(orientationchange in window ? orientationchange : resize, callback);n
完整的例子如下
- rem+js的例子
- rem+vw的例子
- vw的例子
總結
如果對本文有什麼疑問,歡迎留言討論;如果覺得本文對你有幫助,那就趕緊讚賞吧,^_^
最後填一下開頭埋的雷吧,demo在這裡,代碼如下
<div class="p1">n <div class="s1">1</div>n <div class="s2">1</div>n</div>n<div class="p2">n <div class="s5">1</div>n <div class="s6">1</div>n</div> n
.p1 {font-size: 16px; line-height: 32px;}n.s1 {font-size: 2em;}n.s2 {font-size: 2em; line-height: 2em;}nn.p2 {font-size: 16px; line-height: 2;}n.s5 {font-size: 2em;}n.s6 {font-size: 2em; line-height: 2em;} n
先來看第一組的答案
p1:font-size: 16px; line-height: 32pxns1:font-size: 32px; line-height: 32pxns2:font-size: 32px; line-height: 64px n
和你的答案一樣嗎?下面來解釋下
- p1 無需解釋
- s1 em作為字體單位,相對於父元素字體大小;line-height繼承父元素計算值
- s2 em作為行高單位時,相對於自身字體大小
再來看看第二組的答案
p2:font-size: 16px; line-height: 32pxns5:font-size: 32px; line-height: 64pxns6:font-size: 32px; line-height: 64px n
意不意外?驚不驚喜?下面來解釋下
- p2
line-height: 2
自身字體大小的兩倍 - s5 數字無單位行高,繼承原始值,s5的line-height繼承的2,自身字體大小的兩倍
- s6 無需解釋
相關資料
- 使用CSS3 REM 和 VW 打造等比例響應式頁面的便捷工作流
- 從網易與淘寶的font-size思考前端設計稿與工作流
- 移動web適配之rem
- 使用 rem 提供一致的字體大小
原文網址:http://yanhaijing.com/css/2017/09/29/principle-of-rem-layout/
歡迎訂閱我的微信公眾帳號,只推送原創文字。掃碼或搜索:顏海鏡
推薦閱讀: