個人博客原文地址:原文地址 若是文章對您有幫助,您的star是對我最好的鼓勵~css
簡要介紹:前端開發中,靜態網頁一般須要適應不一樣分辨率的設備,經常使用的自適應解決方案包括媒體查詢、百分比、rem和vw/vh等。本文從px單位出發,分析了px在移動端佈局中的不足,接着介紹了幾種不一樣的自適應解決方案。html
- px和視口
- 媒體查詢
- 百分比
- 自適應場景下的rem解決方案
- 經過vw/vh來實現自適應
在靜態網頁中,咱們常常用像素(px)做爲單位,來描述一個元素的寬高以及定位信息。在pc端,一般認爲css中,1px所表示的真實長度是固定的。前端
那麼,px真的是一個設備無關,跟長度單位米和分米同樣是固定大小的嗎?webpack
答案是否認的,下面圖1.1和圖1.2分別表示pc端下和移動端下的顯示結果,在網頁中咱們設置的font-size統一爲16px。css3
圖1.1 pc端下font-size爲16px時的顯示結果git
圖1.2 移動端下font-size爲16px時的顯示結果github
從上面兩幅圖的對比能夠看出,字體都是16px,顯然在pc端中文字正常顯示,而在移動端文字很小,幾乎看不到,說明在css中1px並非固定大小,直觀從咱們發如今移動端1px所表示的長度較小,因此致使文字顯示不清楚。web
那麼css中的1px的真實長度到底由什麼決定呢?npm
爲了理清楚這個概念咱們首先介紹像素和視口的概念瀏覽器
像素是網頁佈局的基礎,一個像素表示了計算機屏幕所能顯示的最小區域,像素分爲兩種類型:css像素和物理像素。
咱們在js或者css代碼中使用的px單位就是指的是css像素,物理像素也稱設備像素,只與設備或者說硬件有關,一樣尺寸的屏幕,設備的密度越高,物理像素也就越多。下表表示css像素和物理像素的具體區別:
css像素 | 爲web開發者提供,在css中使用的一個抽象單位 |
---|---|
物理像素 | 只與設備的硬件密度有關,任何設備的物理像素都是固定的 |
那麼css像素與物理像素的轉換關係是怎麼樣的呢?爲了明確css像素和物理像素的轉換關係,必須先了解視口是什麼。
廣義的視口,是指瀏覽器顯示內容的屏幕區域,狹義的視口包括了佈局視口、視覺視口和理想視口
佈局視口定義了pc網頁在移動端的默認佈局行爲,由於一般pc的分辨率較大,佈局視口默認爲980px。也就是說在不設置網頁的viewport的狀況下,pc端的網頁默認會以佈局視口爲基準,在移動端進行展現。所以咱們能夠明顯看出來,默認爲佈局視口時,根植於pc端的網頁在移動端展現很模糊。
視覺視口表示瀏覽器內看到的網站的顯示區域,用戶能夠經過縮放來查看網頁的顯示內容,從而改變視覺視口。視覺視口的定義,就像拿着一個放大鏡分別從不一樣距離觀察同一個物體,視覺視口僅僅相似於放大鏡中顯示的內容,所以視覺視口不會影響佈局視口的寬度和高度。
理想視口或者應該全稱爲「理想的佈局視口」,在移動設備中就是指設備的分辨率。換句話說,理想視口或者說分辨率就是給定設備物理像素的狀況下,最佳的「佈局視口」。
上述視口中,最重要的是要明確理想視口的概念,在移動端中,理想視口或者說分辨率跟物理像素之間有什麼關係呢?
爲了理清分辨率和物理像素之間的聯繫,咱們介紹一個用DPR(Device pixel ratio)設備像素比來表示,則能夠寫成:
1 DPR = 物理像素/分辨率
複製代碼
在不縮放的狀況下,一個css像素就對應一個dpr,也就是說,在不縮放
1 CSS像素 = 物理像素/分辨率
複製代碼
此外,在移動端的佈局中,咱們能夠經過viewport元標籤來控制佈局,好比通常狀況下,咱們能夠經過下述標籤使得移動端在理想視口下佈局:
<meta id="viewport" name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1; user-scalable=no;">
複製代碼
上述meta標籤的每個屬性的詳細介紹以下:
屬性名 | 取值 | 描述 |
---|---|---|
width | 正整數 | 定義佈局視口的寬度,單位爲像素 |
height | 正整數 | 定義佈局視口的高度,單位爲像素,不多使用 |
initial-scale | [0,10] | 初始縮放比例,1表示不縮放 |
minimum-scale | [0,10] | 最小縮放比例 |
maximum-scale | [0,10] | 最大縮放比例 |
user-scalable | yes/no | 是否容許手動縮放頁面,默認值爲yes |
其中咱們來看width屬性,在移動端佈局時,在meta標籤中咱們會將width設置稱爲device-width,device-width通常是表示分辨率的寬,經過width=device-width的設置咱們就將佈局視口設置成了理想的視口。
上述咱們瞭解到了當經過viewport元標籤,設置佈局視口爲理想視口時,1個css像素能夠表示成:
1 CSS像素 = 物理像素/分辨率
複製代碼
咱們直到,在pc端的佈局視口一般狀況下爲980px,移動端以iphone6爲例,分辨率爲375 * 667,也就是說佈局視口在理想的狀況下爲375px。好比如今咱們有一個750px * 1134px的視覺稿,那麼在pc端,一個css像素能夠以下計算:
PC端: 1 CSS像素 = 物理像素/分辨率 = 750 / 980 =0.76 px
複製代碼
而在iphone6下:
iphone6:1 CSS像素 = 物理像素 /分辨率 = 750 / 375 = 2 px
複製代碼
也就是說在PC端,一個CSS像素能夠用0.76個物理像素來表示,而iphone6中 一個CSS像素表示了2個物理像素。此外不一樣的移動設備分辨率不一樣,也就是1個CSS像素能夠表示的物理像素是不一樣的,所以若是在css中僅僅經過px做爲長度和寬度的單位,形成的結果就是沒法經過一套樣式,實現各端的自適應。
在前面咱們說到,不一樣端的設備下,在css文件中,1px所表示的物理像素的大小是不一樣的,所以經過一套樣式,是沒法實現各端的自適應。由此咱們聯想:
若是一套樣式不行,那麼可否給每一種設備各一套不一樣的樣式來實現自適應的效果?
答案是確定的。
使用@media媒體查詢能夠針對不一樣的媒體類型定義不一樣的樣式,特別是響應式頁面,能夠針對不一樣屏幕的大小,編寫多套樣式,從而達到自適應的效果。舉例來講:
@media screen and (max-width: 960px){
body{
background-color:#FF6699
}
}
@media screen and (max-width: 768px){
body{
background-color:#00FF66;
}
}
@media screen and (max-width: 550px){
body{
background-color:#6633FF;
}
}
@media screen and (max-width: 320px){
body{
background-color:#FFFF00;
}
}
複製代碼
上述的代碼經過媒體查詢定義了幾套樣式,經過max-width設置樣式生效時的最大分辨率,上述的代碼分別對分辨率在0~320px,320px~550px,550px~768px以及768px~960px的屏幕設置了不一樣的背景顏色。
經過媒體查詢,能夠經過給不一樣分辨率的設備編寫不一樣的樣式來實現響應式的佈局,好比咱們爲不一樣分辨率的屏幕,設置不一樣的背景圖片。好比給小屏幕手機設置@2x圖,爲大屏幕手機設置@3x圖,經過媒體查詢就能很方便的實現。
可是媒體查詢的缺點也很明顯,若是在瀏覽器大小改變時,須要改變的樣式太多,那麼多套樣式代碼會很繁瑣。
除了用px結合媒體查詢實現響應式佈局外,咱們也能夠經過百分比單位 " % " 來實現響應式的效果。
好比當瀏覽器的寬度或者高度發生變化時,經過百分比單位,經過百分比單位可使得瀏覽器中的組件的寬和高隨着瀏覽器的變化而變化,從而實現響應式的效果。
爲了瞭解百分比佈局,首先要了解的問題是:
css中的子元素中的百分比(%)究竟是誰的百分比?
直觀的理解,咱們可能會認爲子元素的百分比徹底相對於直接父元素,height百分比相對於height,width百分比相對於width。固然這種理解是正確的,可是根據css的盒式模型,除了height、width屬性外,還具備padding、border、margin等等屬性。那麼這些屬性設置成百分比,是根據父元素的那些屬性呢?此外還有border-radius和translate等屬性中的百分比,又是相對於什麼呢?下面來具體分析。
(1)子元素height和width的百分比
子元素的height或width中使用百分比,是相對於子元素的直接父元素,width相對於父元素的width,height相對於父元素的height。好比:
<div class="parent">
<div class="child"></div>
</div>
複製代碼
若是設置: .father{ width:200px; height:100px; } .child{ width:50%; height:50%; } 展現的效果爲:
(2) top和bottom 、left和right
子元素的top和bottom若是設置百分比,則相對於直接非static定位(默認定位)的父元素的高度,一樣
子元素的left和right若是設置百分比,則相對於直接非static定位(默認定位的)父元素的寬度。
展現的效果爲:
(3)padding
子元素的padding若是設置百分比,不管是垂直方向或者是水平方向,都相對於直接父親元素的width,而與父元素的height無關。
舉例來講:
.parent{
width:200px;
height:100px;
background:green;
}
.child{
width:0px;
height:0px;
background:blue;
color:white;
padding-top:50%;
padding-left:50%;
}
複製代碼
展現的效果爲:
子元素的初始寬高爲0,經過padding能夠將父元素撐大,上圖的藍色部分是一個正方形,且邊長爲100px,說明padding不論寬高,若是設置成百分比都相對於父元素的width。
(4)margin
跟padding同樣,margin也是如此,子元素的margin若是設置成百分比,不管是垂直方向仍是水平方向,都相對於直接父元素的width。這裏就不具體舉例。
(5)border-radius
border-radius不同,若是設置border-radius爲百分比,則是相對於自身的寬度,舉例來講:
<div class="trangle"></div>
複製代碼
設置border-radius爲百分比:
.trangle{
width:100px;
height:100px;
border-radius:50%;
background:blue;
margin-top:10px;
}
複製代碼
展現效果爲:
除了border-radius外,還有好比translate、background-size等都是相對於自身的,這裏就不一一舉例。
百分比單位在佈局上應用仍是很普遍的,這裏介紹一種應用。
好比咱們要實現一個固定長寬比的長方形,好比要實現一個長寬比爲4:3的長方形,咱們能夠根據padding屬性來實現,由於padding不論是垂直方向仍是水平方向,百分比單位都相對於父元素的寬度,所以咱們能夠設置padding-top爲百分比來實現,長寬自適應的長方形:
<div class="trangle"></div>
複製代碼
設置樣式讓其自適應:
.trangle{
height:0;
width:100%;
padding-top:75%;
}
複製代碼
經過設置padding-top:75%,相對比寬度的75%,所以這樣就設置了一個長寬高恆定比例的長方形,具體效果展現以下:
從上述對於百分比單位的介紹咱們很容易看出若是所有使用百分比單位來實現響應式的佈局,有明顯的如下兩個缺點:
(1)計算困難,若是咱們要定義一個元素的寬度和高度,按照設計稿,必須換算成百分比單位。 (2)從小節1能夠看出,各個屬性中若是使用百分比,相對父元素的屬性並非惟一的。好比width和height相對於父元素的width和height,而margin、padding無論垂直仍是水平方向都相對比父元素的寬度、border-radius則是相對於元素自身等等,形成咱們使用百分比單位容易使佈局問題變得複雜。
首先來看,什麼是rem單位。rem是一個靈活的、可擴展的單位,由瀏覽器轉化像素並顯示。與em單位不一樣,rem單位不管嵌套層級如何,都只相對於瀏覽器的根元素(HTML元素)的font-size。默認狀況下,html元素的font-size爲16px,因此:
1 rem = 12px
複製代碼
爲了計算方便,一般能夠將html的font-size設置成:
html{ font-size: 62.5% }
複製代碼
這種狀況下:
1 rem = 10px
複製代碼
rem單位都是相對於根元素html的font-size來決定大小的,根元素的font-size至關於提供了一個基準,當頁面的size發生變化時,只須要改變font-size的值,那麼以rem爲固定單位的元素的大小也會發生響應的變化。 所以,若是經過rem來實現響應式的佈局,只須要根據視圖容器的大小,動態的改變font-size便可。
function refreshRem() {
var docEl = doc.documentElement;
var width = docEl.getBoundingClientRect().width;
var rem = width / 10;
docEl.style.fontSize = rem + 'px';
flexible.rem = win.rem = rem;
}
win.addEventListener('resize', refreshRem);
複製代碼
上述代碼中將視圖容器分爲10份,font-size用十分之一的寬度來表示,最後在header標籤中執行這段代碼,就能夠動態定義font-size的大小,從而1rem在不一樣的視覺容器中表示不一樣的大小,用rem固定單位能夠實現不一樣容器內佈局的自適應。
若是在響應式佈局中使用rem單位,那麼存在一個單位換算的問題,rem2px表示從rem換算成px,這個就不說了,只要rem乘以相應的font-size中的大小,就能換算成px。更多的應用是px2rem,表示的是從px轉化爲rem。
好比給定的視覺稿爲750px(物理像素),若是咱們要將全部的佈局單位都用rem來表示,一種比較笨的辦法就是對全部的height和width等元素,乘以相應的比例,現將視覺稿換算成rem單位,而後一個個的用rem來表示。另外一種比較方便的解決方法就是,在css中咱們仍是用px來表示元素的大小,最後編寫完css代碼以後,將css文件中的全部px單位,轉化成rem單位。
px2rem的原理也很簡單,重點在於預處理以px爲單位的css文件,處理後將全部的px變成rem單位。能夠經過兩種方式來實現:
1) webpack loader的形式:
npm install px2rem-loader
複製代碼
在webpack的配置文件中:
module.exports = {
// ...
module: {
rules: [{
test: /\.css$/,
use: [{
loader: 'style-loader'
}, {
loader: 'css-loader'
}, {
loader: 'px2rem-loader',
// options here
options: {
remUni: 75,
remPrecision: 8
}
}]
}]
}
複製代碼
}
2)webpack中使用postcss plugin
npm install postcss-loader
複製代碼
在webpack的plugin中:
var px2rem = require('postcss-px2rem');
module.exports = {
module: {
loaders: [
{
test: /\.css$/,
loader: "style-loader!css-loader!postcss-loader"
}
]
},
postcss: function() {
return [px2rem({remUnit: 75})];
}
}
複製代碼
網易新聞的移動端頁面使用了rem佈局,具體例子以下:
經過rem單位,能夠實現響應式的佈局,特別是引入相應的postcss相關插件,免去了設計稿中的px到rem的計算。rem單位在國外的一些網站也有使用,這裏所說的rem來實現佈局的缺點,或者說是小缺陷是:
在響應式佈局中,必須經過js來動態控制根元素font-size的大小。
也就是說css樣式和js代碼有必定的耦合性。且必須將改變font-size的代碼放在css樣式以前。
css3中引入了一個新的單位vw/vh,與視圖窗口有關,vw表示相對於視圖窗口的寬度,vh表示相對於視圖窗口高度,除了vw和vh外,還有vmin和vmax兩個相關的單位。各個單位具體的含義以下:
單位 | 含義 |
---|---|
vw | 相對於視窗的寬度,視窗寬度是100vw |
vh | 相對於視窗的高度,視窗高度是100vh |
vmin | vw和vh中的較小值 |
vmax | vw和vh中的較大值 |
這裏咱們發現視窗寬高都是100vw/100vh,那麼vw或者vh,下簡稱vw,很相似百分比單位。vw和%的區別爲:
單位 | 含義 |
---|---|
% | 大部分相對於祖先元素,也有相對於自身的狀況好比(border-radius、translate等) |
vw/vh | 相對於視窗的尺寸 |
從對比中咱們能夠發現,vw單位與百分比相似,單確有區別,前面咱們介紹了百分比單位的換算困難,這裏的vw更像"理想的百分比單位"。任意層級元素,在使用vw單位的狀況下,1vw都等於視圖寬度的百分之一。
一樣的,若是要將px換算成vw單位,很簡單,只要肯定視圖的窗口大小(佈局視口),若是咱們將佈局視口設置成分辨率大小,好比對於iphone6/7 375*667的分辨率,那麼px能夠經過以下方式換算成vw:
1px = (1/375)*100 vw
複製代碼
此外,也能夠經過postcss的相應插件,預處理css作一個自動的轉換,postcss-px-to-viewport能夠自動將px轉化成vw。 postcss-px-to-viewport的默認參數爲:
var defaults = {
viewportWidth: 320,
viewportHeight: 568,
unitPrecision: 5,
viewportUnit: 'vw',
selectorBlackList: [],
minPixelValue: 1,
mediaQuery: false
};
複製代碼
經過指定視窗的寬度和高度,以及換算精度,就能將px轉化成vw。
能夠在https://caniuse.com/ 查看各個版本的瀏覽器對vw單位的支持性。
從上圖咱們發現,絕大多數的瀏覽器支持vw單位,可是ie9-11不支持vmin和vmax,考慮到vmin和vmax單位不經常使用,vw單位在絕大部分高版本瀏覽器內的支持性很好,可是opera瀏覽器總體不支持vw單位,若是須要兼容opera瀏覽器的佈局,不推薦使用vw。
小結:本文介紹在佈局中經常使用的單位,好比px、%、rem和vw等等,以及不一樣的單位在響應式佈局中的優缺點。