Why
最近做了個移動app的宣傳頁,拿到設計稿的時候第一個反應就是必須要在所有的移動裝置解析度下都要保持一致(這不是廢話嘛。。。),正好這陣子瞭解了一下動態rem方案,正好可以用在這個頁面上,話不多少,先看效果
可以看到,最終效果就是在不同的解析度下,展現的效果都是一致的,這裡用的就是今天的主角--動態rem方案,其效果就是,一套css,多個解析度適配,其實原理非常簡單,接下啦就來介紹下如何實現。
How
首先來說下rem,rem單位是基於html的字型大小來進行計算的,比如螢幕的寬度是320畫素,而裡面某個容器的寬度為160畫素,即螢幕寬度的一半。但是rem是基於html的字型的,乍一看這兩組東西沒有啥直接的聯絡,如果想實現自適應,必然就需要通過某種手段把rem基於的html字型大小和螢幕寬度聯絡起來,所以,需要先通過js獲取螢幕寬度(這裡我們就預設螢幕寬度等於視窗寬度),然後將獲取到的寬度作為html的字型大小,再把需要設定寬度的容器設為基於這個寬度的rem值即可。先看下虛擬碼
//1. 獲取螢幕寬度
var width = 呼叫螢幕寬度的api
//2. 將寬度設定為html的字型大小
html { font-size: width }
//3. 在css中給容器設定rem單位的值
.box { width: 0.5rem } //這裡螢幕寬320px,容器寬160px,轉換為rem計算160/320得到寬度為0.5rem
複製程式碼
注意
上面提到的情況其實是最理想的情況,就是設計稿的寬度和螢幕寬度一樣的情況下,是可以的,但是在實際開發中,總不可能針對所有解析度的螢幕設計一套設計稿吧,所以這裡又引入了一個關係,就是設計稿和實際寬度的縮放比例的關係,比如設計稿寬度為640畫素,而除錯裝置的螢幕寬度為320畫素,那麼就存在一個320 /640=0.5的縮放比例,而在頁面開發時,我們是通過設計稿來獲取元素大小的,在頁面上高度就需要去乘一下縮放比例,才能知道我們實際需要這個元素的大小,根據這個思路,再修改一下虛擬碼
//1. 獲取螢幕寬度
var width = 呼叫螢幕寬度的api
//2. 獲取縮放比例
var scale = width / 設計稿寬度
//3. 將寬度設定為html的字型大小
html { font-size: scale * 設計稿寬度}
//4. 在css中給容器設定rem單位的值
.box { width: 0.5rem } //這裡設計稿寬320px,容器寬160px,轉換為rem計算160/320得到寬度為0.5rem
複製程式碼
仔細看一下不難發現,最後得到的html字型還是不變的,只是中間多了個看似無意義的scale,但這樣寫可以理解元素從設計稿到最終展示的轉換過程,實際開發沒有必要這樣寫。。。
可以看出,1rem的大小正好等於螢幕的寬度,但又發現了一個問題,比如某個文字字型大小為16px,1rem為320px,這樣字型大小轉換為rem後,得到的結果就是0.05rem,更甚者,某個元素的margin-right為3px,轉換為rem就是0.009375rem,這樣寫會發現非常麻煩,小數點後面總是寫好多,總結一下原因,就是因為rem的基數太大了,這裡就可以考慮將rem的基數適當的縮小,比如,我們可以把html的字型大小設定為螢幕寬度的十分之一,這樣10rem等於螢幕寬度,頁面元素的計算都可以基於這個縮小後的值來計算。重新寫下虛擬碼
//1. 獲取螢幕寬度
var width = 呼叫螢幕寬度的api
//2. 將寬度設定為html的字型大小
html { font-size: width/10 }
//3. 在css中給容器設定rem單位的值
.box { width: 5rem } //這裡螢幕寬320px,html字型大小為320/10=32,容器寬160px,轉換為rem計算160/32得到寬度為5rem
複製程式碼
這樣修改了一下,是不是看起來更舒服了呢,現在就開始實戰一波
do it
寫一個小demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<script>
var width = document.documentElement.clientWidth;
var css = 'html{' +
'font-size:' + width / 10 + 'px' +
'}';
document.write('<style>' + css + '</style>');
</script>
<style>
* {
margin: 0;
padding: 0;
}
.box {
width: 2rem;
height: 2rem;
margin: 2rem auto;
background: #000;
border-radius: 50%;
}
</style>
<body>
<div class="box">
</div>
</body>
</html>
複製程式碼
程式碼直接放到html檔案中,點開執行,在不同的解析度下,可以看到展示的大小比例都是一樣的。
動態rem方案的核心大致就是這樣,是不是很簡單,接下來,需要自適應的頁面開發時,只需要把px單位全部計算成rem即可
Ask a question
在實際開發中,我們會遇到一個問題,一個頁面,動輒幾十上百個元素,每個元素都有寬高,內外邊距,字型大小,那麼多px單位一個個算成rem,簡直讓人崩潰,10px就能表達的寫法卻可能改成0.33333333rem這種複雜的寫法,是否有什麼辦法能夠讓我偷個懶,我只想在設計稿上量出來多大,就寫多大。當然可以,這邊就引入另外一個概念--css預處理工具,預處理工具能夠幫我們簡化計算,在生成最終的css之前先加工一下,最終得到想要的結果 它甚至引入了函式的概念,類似這樣
function getRem(beforePX){
var resultRem = beforePX / psdWidth
return resultRem
}
css
.box {
width: getRem(100px)
}
複製程式碼
stylus
這邊我選擇stylus作為示例,也可以選sass,語法可以參考官網
stylus工具的用法參照npm
- 全域性安裝stylus 命令列下執行
npm install -g stylus
複製程式碼
- 安裝完成後,在專案目錄下新建一個test.styl檔案,並把css樣式用stylus的語法寫在裡面
將html中的style樣式搬到styl檔案中
index.styl
*
margin: 0
padding: 0
.box
width: 2rem
height: 2rem
margin: 2rem auto
background: green
border-radius: 50%
複製程式碼
命令列工具執行以下命令
stylus -w index.styl -o index.css
複製程式碼
這句命令的意思就是動態監聽index.styl檔案並在同級目錄下生成index.css檔案,然後命令列就可以最小化了(不要關閉!)
這時會發現同級目錄下多了一個index.css檔案,內容如下
* {
margin: 0;
padding: 0;
}
.box {
width: 2rem;
height: 2rem;
margin: 2rem auto;
background: #008000;
border-radius: 50%;
}
複製程式碼
在styl檔案中頂部定義rem計算函式
假設設計稿寬度640px,元素寬度為320px,頁面字型大小基數為640/10=64px,那麼320px最終轉換為rem就是320/64=5rem,參考google,函式定義如下
rem(value) {
return unit(value/64, 'rem');
}
複製程式碼
將計算的工作交給計算機
修改樣式程式碼,所有需要計算rem的地方都換成這個函式的呼叫,引數就是我們在設計稿中實際量出來的大小
rem(value) {
return unit(value/64, 'rem');
}
*
margin: 0
padding: 0
.box
width: rem(128px)
height: rem(128px)
margin: rem(128px) auto
background: green
border-radius: 50%
複製程式碼
儲存修改,再來看看index.css
* {
margin: 0;
padding: 0;
}
.box {
width: 2rem;
height: 2rem;
margin: 2rem auto;
background: #008000;
border-radius: 50%;
}
複製程式碼
可以看到,stylus編譯後全部幫我們計算好了,來看看效果
至此,就通過stylus+rem實現了一個頁面的動態rem方案,在pc上切換螢幕大小時需要手動重新整理,這個方案更適合移動端,並且移動端不存在頁面大小改變的情況,所以這邊沒有考慮,如果有強迫症的話,可以js監聽一下window的resize時間,如有錯誤,歡迎指正拜拜!