響應(yīng)式布局的常用解決方案對比(媒體查詢雪猪、百分比、rem和vw/vh)
本文介紹在布局中常用的單位起愈,比如px只恨、%、rem和vw等等告材,以及不同的單位在響應(yīng)式布局中的優(yōu)缺點坤次。
簡要介紹:前端開發(fā)中古劲,靜態(tài)網(wǎng)頁通常需要適應(yīng)不同分辨率的設(shè)備斥赋,常用的自適應(yīng)解決方案包括媒體查詢、百分比产艾、rem和vw/vh等疤剑。本文從px單位出發(fā)滑绒,分析了px在移動端布局中的不足,接著介紹了幾種不同的自適應(yīng)解決方案隘膘。
- px和視口
- 媒體查詢
- 百分比
- 自適應(yīng)場景下的rem解決方案
- 通過vw/vh來實現(xiàn)自適應(yīng)
一疑故、px和視口
在靜態(tài)網(wǎng)頁中,我們經(jīng)常用像素(px)作為單位弯菊,來描述一個元素的寬高以及定位信息纵势。在pc端,通常認(rèn)為css中,1px所表示的真實長度是固定的管钳。
那么钦铁,px真的是一個設(shè)備無關(guān),跟長度單位米和分米一樣是固定大小的嗎才漆?
答案是否定的牛曹,下面圖1.1和圖1.2分別表示pc端下和移動端下的顯示結(jié)果,在網(wǎng)頁中我們設(shè)置的font-size統(tǒng)一為16px醇滥。
圖1.2 移動端下font-size為16px時的顯示結(jié)果
從上面兩幅圖的對比可以看出黎比,字體都是16px,顯然在pc端中文字正常顯示鸳玩,而在移動端文字很小阅虫,幾乎看不到,說明在css中1px并不是固定大小不跟,直觀從我們發(fā)現(xiàn)在移動端1px所表示的長度較小书妻,所以導(dǎo)致文字顯示不清楚。
那么css中的1px的真實長度到底由什么決定呢躬拢?
為了理清楚這個概念我們首先介紹像素和視口的概念
1. 像素
像素是網(wǎng)頁布局的基礎(chǔ)躲履,一個像素表示了計算機(jī)屏幕所能顯示的最小區(qū)域,像素分為兩種類型:css像素和物理像素聊闯。
我們在js或者css代碼中使用的px單位就是指的是css像素工猜,物理像素也稱設(shè)備像素,只與設(shè)備或者說硬件有關(guān)菱蔬,同樣尺寸的屏幕篷帅,設(shè)備的密度越高,物理像素也就越多拴泌。下表表示css像素和物理像素的具體區(qū)別:
css像素 | 為web開發(fā)者提供魏身,在css中使用的一個抽象單位 |
---|---|
物理像素 | 只與設(shè)備的硬件密度有關(guān),任何設(shè)備的物理像素都是固定的 |
那么css像素與物理像素的轉(zhuǎn)換關(guān)系是怎么樣的呢蚪腐?為了明確css像素和物理像素的轉(zhuǎn)換關(guān)系箭昵,必須先了解視口是什么。
2. 視口
廣義的視口回季,是指瀏覽器顯示內(nèi)容的屏幕區(qū)域家制,狹義的視口包括了布局視口正林、視覺視口和理想視口
(1) 布局視口(layout viewport)
布局視口定義了pc網(wǎng)頁在移動端的默認(rèn)布局行為,因為通常pc的分辨率較大颤殴,布局視口默認(rèn)為980px觅廓。也就是說在不設(shè)置網(wǎng)頁的viewport的情況下,pc端的網(wǎng)頁默認(rèn)會以布局視口為基準(zhǔn)涵但,在移動端進(jìn)行展示杈绸。因此我們可以明顯看出來,默認(rèn)為布局視口時矮瘟,根植于pc端的網(wǎng)頁在移動端展示很模糊蝇棉。
(2) 視覺視口(visual viewport)
視覺視口表示瀏覽器內(nèi)看到的網(wǎng)站的顯示區(qū)域,用戶可以通過縮放來查看網(wǎng)頁的顯示內(nèi)容芥永,從而改變視覺視口篡殷。視覺視口的定義,就像拿著一個放大鏡分別從不同距離觀察同一個物體埋涧,視覺視口僅僅類似于放大鏡中顯示的內(nèi)容板辽,因此視覺視口不會影響布局視口的寬度和高度。
(3) 理想視口(ideal viewport)
理想視口或者應(yīng)該全稱為“理想的布局視口”棘催,在移動設(shè)備中就是指設(shè)備的分辨率劲弦。換句話說,理想視口或者說分辨率就是給定設(shè)備物理像素的情況下醇坝,最佳的“布局視口”邑跪。
上述視口中,最重要的是要明確理想視口的概念呼猪,在移動端中画畅,理想視口或者說分辨率跟物理像素之間有什么關(guān)系呢?
為了理清分辨率和物理像素之間的聯(lián)系宋距,我們介紹一個用DPR(Device pixel ratio)設(shè)備像素比來表示轴踱,則可以寫成:
1 DPR = 物理像素/分辨率
在不縮放的情況下,一個css像素就對應(yīng)一個dpr谚赎,
1 CSS像素 = 物理像素/分辨率
此外淫僻,在移動端的布局中,我們可以通過viewport元標(biāo)簽來控制布局壶唤,比如一般情況下雳灵,我們可以通過下述標(biāo)簽使得移動端在理想視口下布局:
<meta id="viewport" name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1; user-scalable=no;">
上述meta標(biāo)簽的每一個屬性的詳細(xì)介紹如下:
屬性名 | 取值 | 描述 |
---|---|---|
width | 正整數(shù) | 定義布局視口的寬度,單位為像素 |
height | 正整數(shù) | 定義布局視口的高度闸盔,單位為像素悯辙,很少使用 |
initial-scale | [0,10] | 初始縮放比例,1表示不縮放 |
minimum-scale | [0,10] | 最小縮放比例 |
maximum-scale | [0,10] | 最大縮放比例 |
user-scalable | yes/no | 是否允許手動縮放頁面,默認(rèn)值為yes |
其中我們來看width屬性笑撞,在移動端布局時,在meta標(biāo)簽中我們會將width設(shè)置稱為device-width钓觉,device-width一般是表示分辨率的寬茴肥,通過width=device-width的設(shè)置我們就將布局視口設(shè)置成了理想的視口。
3. px與自適應(yīng)
上述我們了解到了當(dāng)通過viewport元標(biāo)簽荡灾,設(shè)置布局視口為理想視口時瓤狐,1個css像素可以表示成:
1 CSS像素 = 物理像素/分辨率
我們直到,在pc端的布局視口通常情況下為980px批幌,移動端以iphone6為例础锐,分辨率為375 * 667,也就是說布局視口在理想的情況下為375px荧缘。比如現(xiàn)在我們有一個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個物理像素。此外不同的移動設(shè)備分辨率不同绸罗,也就是1個CSS像素可以表示的物理像素是不同的意推,因此如果在css中僅僅通過px作為長度和寬度的單位,造成的結(jié)果就是無法通過一套樣式珊蟀,實現(xiàn)各端的自適應(yīng)菊值。
二、媒體查詢
在前面我們說到育灸,不同端的設(shè)備下腻窒,在css文件中,1px所表示的物理像素的大小是不同的磅崭,因此通過一套樣式定页,是無法實現(xiàn)各端的自適應(yīng)。由此我們聯(lián)想:
如果一套樣式不行绽诚,那么能否給每一種設(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è)置樣式生效時的最大分辨率,上述的代碼分別對分辨率在0~320px腰湾,320px~550px雷恃,550px~768px以及768px~960px的屏幕設(shè)置了不同的背景顏色。
通過媒體查詢费坊,可以通過給不同分辨率的設(shè)備編寫不同的樣式來實現(xiàn)響應(yīng)式的布局倒槐,比如我們?yōu)椴煌直媛实钠聊唬O(shè)置不同的背景圖片附井。比如給小屏幕手機(jī)設(shè)置@2x圖讨越,為大屏幕手機(jī)設(shè)置@3x圖,通過媒體查詢就能很方便的實現(xiàn)永毅。
但是媒體查詢的缺點也很明顯把跨,如果在瀏覽器大小改變時,需要改變的樣式太多沼死,那么多套樣式代碼會很繁瑣着逐。
三、百分比
除了用px結(jié)合媒體查詢實現(xiàn)響應(yīng)式布局外意蛀,我們也可以通過百分比單位 " % " 來實現(xiàn)響應(yīng)式的效果滨嘱。
比如當(dāng)瀏覽器的寬度或者高度發(fā)生變化時,通過百分比單位浸间,通過百分比單位可以使得瀏覽器中的組件的寬和高隨著瀏覽器的變化而變化太雨,從而實現(xiàn)響應(yīng)式的效果。
為了了解百分比布局魁蒜,首先要了解的問題是:
css中的子元素中的百分比(%)到底是誰的百分比囊扳?
直觀的理解,我們可能會認(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ù)未螅蠄D的藍(lán)色部分是一個正方形你虹,且邊長為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)用。
比如我們要實現(xiàn)一個固定長寬比的長方形奖亚,比如要實現(xiàn)一個長寬比為4:3的長方形,我們可以根據(jù)padding屬性來實現(xiàn)崎场,因為padding不管是垂直方向還是水平方向,百分比單位都相對于父元素的寬度遂蛀,因此我們可以設(shè)置padding-top為百分比來實現(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è)置了一個長寬高恒定比例的長方形,具體效果展示如下:
<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. 百分比單位缺點
從上述對于百分比單位的介紹我們很容易看出如果全部使用百分比單位來實現(xiàn)響應(yīng)式的布局螃宙,有明顯的以下兩個缺點:
(1)計算困難蛮瞄,如果我們要定義一個元素的寬度和高度,按照設(shè)計稿谆扎,必須換算成百分比單位挂捅。 (2)從小節(jié)1可以看出,各個屬性中如果使用百分比堂湖,相對父元素的屬性并不是唯一的闲先。比如width和height相對于父元素的width和height,而margin无蜂、padding不管垂直還是水平方向都相對比父元素的寬度伺糠、border-radius則是相對于元素自身等等,造成我們使用百分比單位容易使布局問題變得復(fù)雜斥季。
四训桶、自適應(yīng)場景下的rem解決方案
1. rem單位
首先來看,什么是rem單位酣倾。rem是一個靈活的舵揭、可擴(kuò)展的單位,由瀏覽器轉(zhuǎn)化像素并顯示躁锡。與em單位不同午绳,rem單位無論嵌套層級如何,都只相對于瀏覽器的根元素(HTML元素)的font-size映之。默認(rèn)情況下拦焚,html元素的font-size為12px,所以:
1 rem = 12px
為了計算方便惕医,通掣可以將html的font-size設(shè)置成:
html{ font-size: 67.5% }
這種情況下:
1 rem = 10px
2.通過rem來實現(xiàn)響應(yīng)式布局
rem單位都是相對于根元素html的font-size來決定大小的,根元素的font-size相當(dāng)于提供了一個基準(zhǔn),當(dāng)頁面的size發(fā)生變化時抬伺,只需要改變font-size的值螟够,那么以rem為固定單位的元素的大小也會發(fā)生響應(yīng)的變化。 因此峡钓,如果通過rem來實現(xiàn)響應(yīng)式的布局妓笙,只需要根據(jù)視圖容器的大小,動態(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í)行這段代碼拉鹃,就可以動態(tài)定義font-size的大小辈赋,從而1rem在不同的視覺容器中表示不同的大小鲫忍,用rem固定單位可以實現(xiàn)不同容器內(nèi)布局的自適應(yīng)。
3. rem2px和px2rem
如果在響應(yīng)式布局中使用rem單位钥屈,那么存在一個單位換算的問題悟民,rem2px表示從rem換算成px,這個就不說了篷就,只要rem乘以相應(yīng)的font-size中的大小射亏,就能換算成px。更多的應(yīng)用是px2rem竭业,表示的是從px轉(zhuǎn)化為rem智润。
比如給定的視覺稿為750px(物理像素),如果我們要將所有的布局單位都用rem來表示未辆,一種比較笨的辦法就是對所有的height和width等元素窟绷,乘以相應(yīng)的比例,現(xiàn)將視覺稿換算成rem單位鼎姐,然后一個個的用rem來表示钾麸。另一種比較方便的解決方法就是更振,在css中我們還是用px來表示元素的大小炕桨,最后編寫完css代碼之后,將css文件中的所有px單位肯腕,轉(zhuǎn)化成rem單位献宫。
px2rem的原理也很簡單,重點在于預(yù)處理以px為單位的css文件实撒,處理后將所有的px變成rem單位姊途。可以通過兩種方式來實現(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)易新聞的移動端頁面使用了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 布局的缺點
通過rem單位捷兰,可以實現(xiàn)響應(yīng)式的布局,特別是引入相應(yīng)的postcss相關(guān)插件负敏,免去了設(shè)計稿中的px到rem的計算贡茅。rem單位在國外的一些網(wǎng)站也有使用,這里所說的rem來實現(xiàn)布局的缺點其做,或者說是小缺陷是:
在響應(yīng)式布局中顶考,必須通過js來動態(tài)控制根元素font-size的大小。
也就是說css樣式和js代碼有一定的耦合性妖泄。且必須將改變font-size的代碼放在css樣式之前驹沿。
五. 通過vw/vh來實現(xiàn)自適應(yīng)
1. 什么是vw/vh ?
css3中引入了一個新的單位vw/vh,與視圖窗口有關(guān)蹈胡,vw表示相對于視圖窗口的寬度渊季,vh表示相對于視圖窗口高度朋蔫,除了vw和vh外,還有vmin和vmax兩個相關(guān)的單位却汉。各個單位具體的含義如下:
單位 | 含義 |
---|---|
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做一個自動的轉(zhuǎn)換,postcss-px-to-viewport可以自動將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/ 查看各個版本的瀏覽器對vw單位的支持性却妨。