(超詳細(xì))媒體查詢、百分比、rem和vw/vh等布局對比解析
響應(yīng)式布局的常用解決方案對比(媒體查詢、百分比、rem和vw/vh)
本文介紹在布局中常用的單位,比如px、%、rem和vw等等,以及不同的單位在響應(yīng)式布局中的優(yōu)缺點(diǎn)。
簡要介紹:前端開發(fā)中,靜態(tài)網(wǎng)頁通常需要適應(yīng)不同分辨率的設(shè)備,常用的自適應(yīng)解決方案包括媒體查詢、百分比、rem和vw/vh等。本文從px單位出發(fā),分析了px在移動(dòng)端布局中的不足,接著介紹了幾種不同的自適應(yīng)解決方案。
- px和視口
- 媒體查詢
- 百分比
- 自適應(yīng)場景下的rem解決方案
- 通過vw/vh來實(shí)現(xiàn)自適應(yīng)
一、px和視口
在靜態(tài)網(wǎng)頁中,我們經(jīng)常用像素(px)作為單位,來描述一個(gè)元素的寬高以及定位信息。在pc端,通常認(rèn)為css中,1px所表示的真實(shí)長度是固定的。
那么,px真的是一個(gè)設(shè)備無關(guān),跟長度單位米和分米一樣是固定大小的嗎?
答案是否定的,下面圖1.1和圖1.2分別表示pc端下和移動(dòng)端下的顯示結(jié)果,在網(wǎng)頁中我們設(shè)置的font-size統(tǒng)一為16px。
圖1.2 移動(dòng)端下font-size為16px時(shí)的顯示結(jié)果
從上面兩幅圖的對比可以看出,字體都是16px,顯然在pc端中文字正常顯示,而在移動(dòng)端文字很小,幾乎看不到,說明在css中1px并不是固定大小,直到我們發(fā)現(xiàn)在移動(dòng)端1px所表示的長度較小,所以導(dǎo)致文字顯示不清楚。
那么css中的1px的真實(shí)長度到底由什么決定呢?
為了理清楚這個(gè)概念我們首先介紹像素和視口的概念
1. 像素
像素是網(wǎng)頁布局的基礎(chǔ),一個(gè)像素表示了計(jì)算機(jī)屏幕所能顯示的最小區(qū)域,像素分為兩種類型:css像素和物理像素。
我們在js或者css代碼中使用的px單位就是指的是css像素,物理像素也稱設(shè)備像素,只與設(shè)備或者說硬件有關(guān),同樣尺寸的屏幕,設(shè)備的密度越高,物理像素也就越多。下表表示css像素和物理像素的具體區(qū)別:
| css像素 | 為web開發(fā)者提供,在css中使用的一個(gè)抽象單位 |
|---|---|
| 物理像素 | 只與設(shè)備的硬件密度有關(guān),任何設(shè)備的物理像素都是固定的 |
那么css像素與物理像素的轉(zhuǎn)換關(guān)系是怎么樣的呢?為了明確css像素和物理像素的轉(zhuǎn)換關(guān)系,必須先了解視口是什么。
2. 視口
廣義的視口,是指瀏覽器顯示內(nèi)容的屏幕區(qū)域,狹義的視口包括了布局視口、視覺視口和理想視口
(1) 布局視口(layout viewport)
布局視口定義了pc網(wǎng)頁在移動(dòng)端的默認(rèn)布局行為,因?yàn)橥ǔc的分辨率較大,布局視口默認(rèn)為980px。也就是說在不設(shè)置網(wǎng)頁的viewport的情況下,pc端的網(wǎng)頁默認(rèn)會(huì)以布局視口為基準(zhǔn),在移動(dòng)端進(jìn)行展示。因此我們可以明顯看出來,默認(rèn)為布局視口時(shí),根植于pc端的網(wǎng)頁在移動(dòng)端展示很模糊。
(2) 視覺視口(visual viewport)
視覺視口表示瀏覽器內(nèi)看到的網(wǎng)站的顯示區(qū)域,用戶可以通過縮放來查看網(wǎng)頁的顯示內(nèi)容,從而改變視覺視口。視覺視口的定義,就像拿著一個(gè)放大鏡分別從不同距離觀察同一個(gè)物體,視覺視口僅僅類似于放大鏡中顯示的內(nèi)容,因此視覺視口不會(huì)影響布局視口的寬度和高度。
(3) 理想視口(ideal viewport)
理想視口或者應(yīng)該全稱為“理想的布局視口”,在移動(dòng)設(shè)備中就是指設(shè)備的分辨率。換句話說,理想視口或者說分辨率就是給定設(shè)備物理像素的情況下,最佳的“布局視口”。
上述視口中,最重要的是要明確理想視口的概念,在移動(dòng)端中,理想視口或者說分辨率跟物理像素之間有什么關(guān)系呢?
為了理清分辨率和物理像素之間的聯(lián)系,我們介紹一個(gè)用DPR(Device pixel ratio)設(shè)備像素比來表示,則可以寫成:
1 DPR = 物理像素/分辨率
在不縮放的情況下,一個(gè)css像素就對應(yīng)一個(gè)dpr,
1 CSS像素 = 物理像素/分辨率
此外,在移動(dòng)端的布局中,我們可以通過viewport元標(biāo)簽來控制布局,比如一般情況下,我們可以通過下述標(biāo)簽使得移動(dòng)端在理想視口下布局:
<meta id="viewport" name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1; user-scalable=no;">
上述meta標(biāo)簽的每一個(gè)屬性的詳細(xì)介紹如下:
| 屬性名 | 取值 | 描述 |
|---|---|---|
| width | 正整數(shù) | 定義布局視口的寬度,單位為像素 |
| height | 正整數(shù) | 定義布局視口的高度,單位為像素,很少使用 |
| initial-scale | [0,10] | 初始縮放比例,1表示不縮放 |
| minimum-scale | [0,10] | 最小縮放比例 |
| maximum-scale | [0,10] | 最大縮放比例 |
| user-scalable | yes/no | 是否允許手動(dòng)縮放頁面,默認(rèn)值為yes |
其中我們來看width屬性,在移動(dòng)端布局時(shí),在meta標(biāo)簽中我們會(huì)將width設(shè)置稱為device-width,device-width一般是表示分辨率的寬,通過width=device-width的設(shè)置我們就將布局視口設(shè)置成了理想的視口。
3. px與自適應(yīng)
上述我們了解到了當(dāng)通過viewport元標(biāo)簽,設(shè)置布局視口為理想視口時(shí),1個(gè)css像素可以表示成:
1 CSS像素 = 物理像素/分辨率
我們直到,在pc端的布局視口通常情況下為980px,移動(dòng)端以iphone6為例,分辨率為375 * 667,也就是說布局視口在理想的情況下為375px。比如現(xiàn)在我們有一個(gè)750px * 1134px的視覺稿,那么在pc端,一個(gè)css像素可以如下計(jì)算:
PC端: 1 CSS像素 = 物理像素/分辨率 = 750 / 980 =0.76 px
而在iphone6下:
iphone6:1 CSS像素 = 物理像素 /分辨率 = 750 / 375 = 2 px
也就是說在PC端,一個(gè)CSS像素可以用0.76個(gè)物理像素來表示,而iphone6中 一個(gè)CSS像素表示了2個(gè)物理像素。此外不同的移動(dòng)設(shè)備分辨率不同,也就是1個(gè)CSS像素可以表示的物理像素是不同的,因此如果在css中僅僅通過px作為長度和寬度的單位,造成的結(jié)果就是無法通過一套樣式,實(shí)現(xiàn)各端的自適應(yīng)。
二、媒體查詢
在前面我們說到,不同端的設(shè)備下,在css文件中,1px所表示的物理像素的大小是不同的,因此通過一套樣式,是無法實(shí)現(xiàn)各端的自適應(yīng)。由此我們聯(lián)想:
如果一套樣式不行,那么能否給每一種設(shè)備各一套不同的樣式來實(shí)現(xiàn)自適應(yīng)的效果?
答案是肯定的。
使用@media媒體查詢可以針對不同的媒體類型定義不同的樣式,特別是響應(yīng)式頁面,可以針對不同屏幕的大小,編寫多套樣式,從而達(dá)到自適應(yīng)的效果。舉例來說:
@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設(shè)置樣式生效時(shí)的最大分辨率,上述的代碼分別對分辨率在0~320px,320px~550px,550px~768px以及768px~960px的屏幕設(shè)置了不同的背景顏色。
通過媒體查詢,可以通過給不同分辨率的設(shè)備編寫不同的樣式來實(shí)現(xiàn)響應(yīng)式的布局,比如我們?yōu)椴煌直媛实钠聊?,設(shè)置不同的背景圖片。比如給小屏幕手機(jī)設(shè)置@2x圖,為大屏幕手機(jī)設(shè)置@3x圖,通過媒體查詢就能很方便的實(shí)現(xiàn)。
但是媒體查詢的缺點(diǎn)也很明顯,如果在瀏覽器大小改變時(shí),需要改變的樣式太多,那么多套樣式代碼會(huì)很繁瑣。
三、百分比
除了用px結(jié)合媒體查詢實(shí)現(xiàn)響應(yīng)式布局外,我們也可以通過百分比單位 " % " 來實(shí)現(xiàn)響應(yīng)式的效果。
比如當(dāng)瀏覽器的寬度或者高度發(fā)生變化時(shí),通過百分比單位,通過百分比單位可以使得瀏覽器中的組件的寬和高隨著瀏覽器的變化而變化,從而實(shí)現(xiàn)響應(yīng)式的效果。
為了了解百分比布局,首先要了解的問題是:
css中的子元素中的百分比(%)到底是誰的百分比?
直觀的理解,我們可能會(huì)認(rèn)為子元素的百分比完全相對于直接父元素,height百分比相對于height,width百分比相對于width。當(dāng)然這種理解是正確的,但是根據(jù)css的盒式模型,除了height、width屬性外,還具有padding、border、margin等等屬性。那么這些屬性設(shè)置成百分比,是根據(jù)父元素的那些屬性呢?此外還有border-radius和translate等屬性中的百分比,又是相對于什么呢?下面來具體分析。
1. 百分比的具體分析
(1)子元素height和width的百分比
子元素的height或width中使用百分比,是相對于子元素的直接父元素,width相對于父元素的width,height相對于父元素的height。比如:
<div class="parent">
<div class="child"></div>
</div>
如果設(shè)置: .father{ width:200px; height:100px; } .child{ width:50%; height:50%; } 展示的效果為: [圖片上傳失敗...(image-c88b47-1530536154428)]
(2) top和bottom 、left和right
子元素的top和bottom如果設(shè)置百分比,則相對于直接非static定位(默認(rèn)定位)的父元素的高度,同樣
子元素的left和right如果設(shè)置百分比,則相對于直接非static定位(默認(rèn)定位的)父元素的寬度。
(3)padding
子元素的padding如果設(shè)置百分比,不論是垂直方向或者是水平方向,都相對于直接父親元素的width,而與父元素的height無關(guān)。
舉例來說:
.parent{
width:200px;
height:100px;
background:green;
}
.child{
width:0px;
height:0px;
background:blue;
color:white;
padding-top:50%;
padding-left:50%;
}
子元素的初始寬高為0,通過padding可以將父元素?fù)未?,上圖的藍(lán)色部分是一個(gè)正方形,且邊長為100px,說明padding不論寬高,如果設(shè)置成百分比都相對于父元素的width。
(4)margin
跟padding一樣,margin也是如此,子元素的margin如果設(shè)置成百分比,不論是垂直方向還是水平方向,都相對于直接父元素的width。這里就不具體舉例。
(5)border-radius
border-radius不一樣,如果設(shè)置border-radius為百分比,則是相對于自身的寬度,舉例來說:
<div class="trangle"></div>
設(shè)置border-radius為百分比:
.trangle{
width:100px;
height:100px;
border-radius:50%;
background:blue;
margin-top:10px;
}
除了border-radius外,還有比如translate、background-size等都是相對于自身的,這里就不一一舉例。
2. 百分比單位布局應(yīng)用
百分比單位在布局上應(yīng)用還是很廣泛的,這里介紹一種應(yīng)用。
比如我們要實(shí)現(xiàn)一個(gè)固定長寬比的長方形,比如要實(shí)現(xiàn)一個(gè)長寬比為4:3的長方形,我們可以根據(jù)padding屬性來實(shí)現(xiàn),因?yàn)閜adding不管是垂直方向還是水平方向,百分比單位都相對于父元素的寬度,因此我們可以設(shè)置padding-top為百分比來實(shí)現(xiàn),長寬自適應(yīng)的長方形:
<div class="trangle"></div>
設(shè)置樣式讓其自適應(yīng):
.trangle{
height:0;
width:100%;
padding-top:75%;
}
通過設(shè)置padding-top:75%,相對比寬度的75%,因此這樣就設(shè)置了一個(gè)長寬高恒定比例的長方形,具體效果展示如下:
<figure style="display: block; margin: 22px auto; text-align: center;">
<figcaption style="display: block; text-align: center; font-size: 1rem; line-height: 1.6; color: rgb(144, 144, 144); margin-top: 2px;"></figcaption>
</figure>
3. 百分比單位缺點(diǎn)
從上述對于百分比單位的介紹我們很容易看出如果全部使用百分比單位來實(shí)現(xiàn)響應(yīng)式的布局,有明顯的以下兩個(gè)缺點(diǎn):
(1)計(jì)算困難,如果我們要定義一個(gè)元素的寬度和高度,按照設(shè)計(jì)稿,必須換算成百分比單位。 (2)從小節(jié)1可以看出,各個(gè)屬性中如果使用百分比,相對父元素的屬性并不是唯一的。比如width和height相對于父元素的width和height,而margin、padding不管垂直還是水平方向都相對比父元素的寬度、border-radius則是相對于元素自身等等,造成我們使用百分比單位容易使布局問題變得復(fù)雜。
四、自適應(yīng)場景下的rem解決方案
1. rem單位
首先來看,什么是rem單位。rem是一個(gè)靈活的、可擴(kuò)展的單位,由瀏覽器轉(zhuǎn)化像素并顯示。與em單位不同,rem單位無論嵌套層級如何,都只相對于瀏覽器的根元素(HTML元素)的font-size。默認(rèn)情況下,html元素的font-size為12px,所以:
1 rem = 12px
為了計(jì)算方便,通??梢詫tml的font-size設(shè)置成:
html{ font-size: 67.5% }
這種情況下:
1 rem = 10px
2.通過rem來實(shí)現(xiàn)響應(yīng)式布局
rem單位都是相對于根元素html的font-size來決定大小的,根元素的font-size相當(dāng)于提供了一個(gè)基準(zhǔn),當(dāng)頁面的size發(fā)生變化時(shí),只需要改變font-size的值,那么以rem為固定單位的元素的大小也會(huì)發(fā)生響應(yīng)的變化。 因此,如果通過rem來實(shí)現(xiàn)響應(yīng)式的布局,只需要根據(jù)視圖容器的大小,動(dòng)態(tài)的改變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標(biāo)簽中執(zhí)行這段代碼,就可以動(dòng)態(tài)定義font-size的大小,從而1rem在不同的視覺容器中表示不同的大小,用rem固定單位可以實(shí)現(xiàn)不同容器內(nèi)布局的自適應(yīng)。
3. rem2px和px2rem
如果在響應(yīng)式布局中使用rem單位,那么存在一個(gè)單位換算的問題,rem2px表示從rem換算成px,這個(gè)就不說了,只要rem乘以相應(yīng)的font-size中的大小,就能換算成px。更多的應(yīng)用是px2rem,表示的是從px轉(zhuǎn)化為rem。
比如給定的視覺稿為750px(物理像素),如果我們要將所有的布局單位都用rem來表示,一種比較笨的辦法就是對所有的height和width等元素,乘以相應(yīng)的比例,現(xiàn)將視覺稿換算成rem單位,然后一個(gè)個(gè)的用rem來表示。另一種比較方便的解決方法就是,在css中我們還是用px來表示元素的大小,最后編寫完css代碼之后,將css文件中的所有px單位,轉(zhuǎn)化成rem單位。
px2rem的原理也很簡單,重點(diǎn)在于預(yù)處理以px為單位的css文件,處理后將所有的px變成rem單位??梢酝ㄟ^兩種方式來實(shí)現(xiàn):
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})];
}
}
4. rem 布局應(yīng)用舉例
網(wǎng)易新聞的移動(dòng)端頁面使用了rem布局,具體例子如下:
<figure style="display: block; margin: 22px auto; text-align: center;">
<figcaption style="display: block; text-align: center; font-size: 1rem; line-height: 1.6; color: rgb(144, 144, 144); margin-top: 2px;"></figcaption>
</figure>
5. rem 布局的缺點(diǎn)
通過rem單位,可以實(shí)現(xiàn)響應(yīng)式的布局,特別是引入相應(yīng)的postcss相關(guān)插件,免去了設(shè)計(jì)稿中的px到rem的計(jì)算。rem單位在國外的一些網(wǎng)站也有使用,這里所說的rem來實(shí)現(xiàn)布局的缺點(diǎn),或者說是小缺陷是:
在響應(yīng)式布局中,必須通過js來動(dòng)態(tài)控制根元素font-size的大小。
也就是說css樣式和js代碼有一定的耦合性。且必須將改變font-size的代碼放在css樣式之前。
五. 通過vw/vh來實(shí)現(xiàn)自適應(yīng)
1. 什么是vw/vh ?
css3中引入了一個(gè)新的單位vw/vh,與視圖窗口有關(guān),vw表示相對于視圖窗口的寬度,vh表示相對于視圖窗口高度,除了vw和vh外,還有vmin和vmax兩個(gè)相關(guān)的單位。各個(gè)單位具體的含義如下:
| 單位 | 含義 |
|---|---|
| vw | 相對于視窗的寬度,視窗寬度是100vw |
| vh | 相對于視窗的高度,視窗高度是100vh |
| vmin | vw和vh中的較小值 |
| vmax | vw和vh中的較大值 |
這里我們發(fā)現(xiàn)視窗寬高都是100vw/100vh,那么vw或者vh,下簡稱vw,很類似百分比單位。vw和%的區(qū)別為:
| 單位 | 含義 |
|---|---|
| % | 大部分相對于祖先元素,也有相對于自身的情況比如(border-radius、translate等) |
| vw/vh | 相對于視窗的尺寸 |
從對比中我們可以發(fā)現(xiàn),vw單位與百分比類似,單確有區(qū)別,前面我們介紹了百分比單位的換算困難,這里的vw更像"理想的百分比單位"。任意層級元素,在使用vw單位的情況下,1vw都等于視圖寬度的百分之一。
2. vw單位換算
同樣的,如果要將px換算成vw單位,很簡單,只要確定視圖的窗口大小(布局視口),如果我們將布局視口設(shè)置成分辨率大小,比如對于iphone6/7 375*667的分辨率,那么px可以通過如下方式換算成vw:
1px = (1/375)*100 vw
此外,也可以通過postcss的相應(yīng)插件,預(yù)處理css做一個(gè)自動(dòng)的轉(zhuǎn)換,postcss-px-to-viewport可以自動(dòng)將px轉(zhuǎn)化成vw。 postcss-px-to-viewport的默認(rèn)參數(shù)為:
var defaults = {
viewportWidth: 320,
viewportHeight: 568,
unitPrecision: 5,
viewportUnit: 'vw',
selectorBlackList: [],
minPixelValue: 1,
mediaQuery: false
};
通過指定視窗的寬度和高度,以及換算精度,就能將px轉(zhuǎn)化成vw。
3. vw/vh單位的兼容性
可以在https://caniuse.com/ 查看各個(gè)版本的瀏覽器對vw單位的支持性。
文章出處:http://www.itdecent.cn/p/ba26509bc5b3, 感謝作者大大!