前端圈真亂,這話一點(diǎn)不假。但亂也亂的好處,亂則生變,有變化才有進(jìn)步。今天還是老調(diào)重談,聊聊移動端頁面的適配。因?yàn)閷τ谝幻肚岸硕?,天天和頁面打交道(H5頁面),那么布局的活總是少不了,這也將面臨不同終端的適配問題。不知道你是否和我一樣,頁面布局總是或多或少會有一些蛋疼的事情發(fā)生。如果是的話,建議你花點(diǎn)時間閱讀完下面我扯蛋的東東。
Flexible承載的使命
Flexible到今天也有幾年的歷史了,解救了很多同學(xué)針對于H5頁面布局的適配問題。而這套方案也相對而言是一個較為成熟的方案。簡單的回憶一下,當(dāng)初為了能讓頁面更好的適配各種不同的終端,通過Hack手段來根據(jù)設(shè)備的dpr 值相應(yīng)改變<meta> 標(biāo)簽中viewport 的值:
<!-- dpr = 1-->
<meta name="viewport" content="initial-scale=scale,maximum-scale=scale,minimum-scale=scale,user-scalable=no">
<!-- dpr = 2-->
<meta name="viewport" content="initial-scale=0.5,maximum-scale=0.5,minimum-scale=0.5,user-scalable=no">
<!-- dpr = 3-->
<meta name="viewport" content="initial-scale=0.3333333333,maximum-scale=0.3333333333,minimum-scale=0.3333333333,user-scalable=no">
從而讓頁面達(dá)么縮放的效果,也變相的實(shí)現(xiàn)頁面的適配功能。而其主要的思想有三點(diǎn):
- 根據(jù)
dpr 的值來修改viewport 實(shí)現(xiàn)1px 的線
- 根據(jù)
dpr 的值來修改html 的font-size ,從而使用rem 實(shí)現(xiàn)等比縮放
- 使用Hack手段用
rem 模擬vw 特性
有關(guān)于Flexible方案實(shí)現(xiàn)適配,在2015年雙十一之后做過這方面的技術(shù)文檔分享,感興趣的同學(xué)可以移步閱讀《使用Flexible實(shí)現(xiàn)手淘H5頁面的終端適配》一文。雖然Flexible解決了適配終端很多問題,但它并不是萬能的,也不是最優(yōu)秀的,他還是存在一些問題的,比如iframe 的引用,有時候就把我們自己給埋進(jìn)去了。針對其中的一些不足之處,有些同學(xué)對其進(jìn)行過相關(guān)的改造,在網(wǎng)上搜索能找到相關(guān)的方案。
那么時代在變化,前端技術(shù)在不斷的變化,試問:Flexible還是最佳方案?Flexible還有存在的必要嗎? 最近一直在探討這方面,這里先告訴大家Flexible已經(jīng)完成了他自身的歷史使命,我們可以放下Flexible,擁抱新的變化。接下來的內(nèi)容,我將分享一下我最近自己探討的新的適配方案,或許很多團(tuán)隊(duì)同學(xué)已經(jīng)開始使用了,如果有不對之處,希望能得到大嬸們的指正;如果您有更好的方案,希望能一起分享一起探討。
先上菜,再嘮嗑
先上個二維碼:
你可以使用手淘App、優(yōu)酷APP、各終端自帶的瀏覽器、UC瀏覽器、QQ瀏覽器、Safari瀏覽器和Chrome瀏覽器掃描上面的二維碼,您看到相應(yīng)的效果:

iPhone系列效果

部分Android效果
注:如果掃上面的二維碼沒有任何效果,你可以點(diǎn)擊這里,打開在線頁面,重新生成你的設(shè)備能識別的二維碼號 。
上面的Demo,測試了Top30的機(jī)型。目前未得到支持的:
品牌 |
型號 |
系統(tǒng)版本 |
分辨率 |
屏幕尺寸 |
手淘APP |
優(yōu)酷APP |
原生瀏覽器 |
QQ瀏覽器 |
UC瀏覽器 |
Chrome瀏覽器 |
華為 |
Mate9 |
Android7.0 |
1080 x 1920 |
5英寸 |
Yes |
Yes |
No |
Yes |
Yes |
Yes |
華為 |
Mate7 |
Android4.2 |
1080 x 1920 |
5.2英寸 |
Yes |
Yes |
No |
Yes |
Yes |
Yes |
魅族 |
Mx4 (M460 移動4G) |
Android4.4.2 |
1152 x 1920 |
5.36英寸 |
Yes |
No |
No |
Yes |
Yes |
Yes |
Oppo |
R7007 |
Android4.3 |
1280 x 720 |
5英寸 |
Yes |
No |
No |
Yes |
Yes |
No |
三星 |
N9008 (Galaxy Note3) |
Android4.4.2 |
1080 x 1920 |
5.7英寸 |
Yes |
No |
Yes |
Yes |
Yes |
Yes |
華碩 |
ZenFone5(x86) |
Android4.3 |
720 x 280 |
5英寸 |
No |
No |
No |
Yes |
No |
No |
Top30機(jī)型中不在列表中的,將看到的效果如上圖所示。至于敢不敢用,這就得看親了。必竟第一個吃螃蟹的人是需要一定的勇氣!(^_^)
適配方案
前面給大家介紹了這個方案目前得到的支持情況以及效果。也扯了不少廢話,接下來進(jìn)入正題吧。
在移動端布局,我們需要面對兩個最為重要的問題:
- 各終端下的適配問題
- Retina屏的細(xì)節(jié)處理
不同的終端,我們面對的屏幕分辨率、DPR、1px 、2x 圖等一系列的問題。那么這個布局方案也是針對性的解決這些問題,只不過解決這些問題不再是使用Hack手段來處理,而是直接使用原生的CSS技術(shù)來處理的。
適配終端
首要解決的是適配終端?;叵胍幌?,以前的Flexible方案是通過JavaScript來模擬vw 的特性,那么到今天為止,vw 已經(jīng)得到了眾多瀏覽器的支持,也就是說,可以直接考慮將vw 單位運(yùn)用于我們的適配布局中。
眾所周知,vw 是基于Viewport視窗的長度單位,這里的視窗(Viewport)指的就是瀏覽器可視化的區(qū)域,而這個可視區(qū)域是window.innerWidth/window.innerHeight 的大小。用下圖簡單的來示意一下:

因?yàn)閂iewport涉及到的知識點(diǎn)很多,要介紹清楚這方面的知識,都需要幾篇文章來進(jìn)行闡述。@PPK大神有兩篇文章詳細(xì)介紹了這方面的知識。中文可以移步這里進(jìn)行閱讀。
在CSS Values and Units Module Level 3中和Viewport相關(guān)的單位有四個,分別為vw 、vh 、vmin 和vmax 。
vw :是Viewport's width的簡寫,1vw 等于window.innerWidth 的1%
vh :和vw 類似,是Viewport's height的簡寫,1vh 等于window.innerHeihgt 的1%
vmin :vmin 的值是當(dāng)前vw 和vh 中較小的值
vmax :vmax 的值是當(dāng)前vw 和vh 中較大的值
vmin 和vmax 是根據(jù)Viewport中長度偏大的那個維度值計(jì)算出來的,如果window.innerHeight > window.innerWidth 則vmin 取百分之一的window.innerWidth ,vmax 取百分之一的window.innerHeight 計(jì)算。
還是用一張圖來示意吧,一圖勝于千言萬語:

所以在這個方案中大膽的使用vw 來替代以前Flexible中的rem 縮放方案。先來回歸到我們的實(shí)際業(yè)務(wù)中來。目前出視覺設(shè)計(jì)稿,我們都是使用750px 寬度的,從上面的原理來看,那么100vw = 750px ,即1vw = 7.5px 。那么我們可以根據(jù)設(shè)計(jì)圖上的px 值直接轉(zhuǎn)換成對應(yīng)的vw 值??吹竭@里,很多同學(xué)開始感到崩潰,又要計(jì)算,能不能簡便一點(diǎn),能不能再簡單一點(diǎn),其實(shí)是可以的,我們可以使用PostCSS的插件postcss-px-to-viewport,讓我們可以直接在代碼中寫px ,比如:
[w-369]{
width: 369px;
}
[w-369] h2 span {
background: #FF5000;
color: #fff;
display: inline-block;
border-radius: 4px;
font-size: 20px;
text-shadow: 0 2px 2px #FF5000;
padding: 2px 5px;
margin-right: 5px;
}
PostCSS編譯之后就是我們所需要的帶vw 代碼:
[w-369] {
width: 49.2vw;
}
[w-369] h2 span {
background: #ff5000;
color: #fff;
display: inline-block;
border-radius: .53333vw;
text-shadow: 0 0.26667vw 0.26667vw #ff5000;
padding: .26667vw .66667vw;
}
[w-369] h2 span,
[w-369] i {
font-size: 2.66667vw;
margin-right: .66667vw;
}
在實(shí)際使用的時候,你可以對該插件進(jìn)行相關(guān)的參數(shù)配置:
"postcss-px-to-viewport": {
viewportWidth: 750,
viewportHeight: 1334,
unitPrecision: 5,
viewportUnit: 'vw',
selectorBlackList: [],
minPixelValue: 1,
mediaQuery: false
}
假設(shè)你的設(shè)計(jì)稿不是750px 而是1125px ,那么你就可以修改vewportWidth 的值。有關(guān)于該插件的詳細(xì)介紹,可以閱讀其官方使用文檔。
上面解決了px 到vw 的轉(zhuǎn)換計(jì)算。那么在哪些地方可以使用vw 來適配我們的頁面。根據(jù)相關(guān)的測試:
- 容器適配,可以使用
vw
- 文本的適配,可以使用
vw
- 大于
1px 的邊框、圓角、陰影都可以使用vw
- 內(nèi)距和外距,可以使用
vw
另外有一個細(xì)節(jié)需要特別的提出,比如我們有一個這樣的設(shè)計(jì):

如果我們直接使用:
[w-188-246] {
width: 188px;
}
[w-187-246]{
width: 187px
}
最終的效果會造成[w-187-246] 容器的高度小于[w-188-246] 容器的高度。這個時候我們就需要考慮到容器的長寬比縮放。這方面的方案很多,但我還是推薦工具化來處理,這里推薦@一絲 姐姐寫的一個PostCSS插件postcss-aspect-ratio-mini。這個插件使用很簡單,不需要做任何的配置,你只需要本地安裝一下就OK。使用的時候如下:
[aspectratio] {
position: relative;
}
[aspectratio]::before {
content: '';
display: block;
width: 1px;
margin-left: -1px;
height: 0;
}
[aspectratio-content] {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
}
[aspectratio][aspect-ratio="188/246"]{
aspect-ratio: '188:246';
}
編譯出來:
[aspectratio][aspect-ratio="188/246"]:before {
padding-top: 130.85106382978725%;
}
這樣就可以完美的實(shí)現(xiàn)長寬比的效果。有關(guān)于這方面的原理在這里不做過多闡述,感興趣的話可以閱讀早前整理的文章:
目前采用PostCSS插件只是一個過渡階段,在將來我們可以直接在CSS中使用aspect-ratio 屬性來實(shí)現(xiàn)長寬比。
解決1px 方案
前面提到過,對于1px 是不建議將其轉(zhuǎn)換成對應(yīng)的vw 單位的,但在Retina下,我們始終是需要面對如何解決1px 的問題。在《再談Retina下1px 的解決方案》文章中提供了多種解決1px 的方案。在這里的話,個人推薦另外一種解決1px 的方案。依舊是使用PostCSS插件,解決1px 可以使用postcss-write-svg。
使用postcss-write-svg你可以通過border-image 或者background-image 兩種方式來處理。比如:
@svg 1px-border {
height: 2px;
@rect {
fill: var(--color, black);
width: 100%;
height: 50%;
}
}
.example {
border: 1px solid transparent;
border-image: svg(1px-border param(--color #00b1ff)) 2 2 stretch;
}
這樣PostCSS會自動幫你把CSS編譯出來:
.example {
border: 1px solid transparent;
border-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www./2000/svg' height='2px'%3E%3Crect fill='%2300b1ff' width='100%25' height='50%25'/%3E%3C/svg%3E") 2 2 stretch;
}
使用PostCSS的插件是不是比我們修改圖片要來得簡單與方便。
上面演示的是使用border-image 方式,除此之外還可以使用background-image 來實(shí)現(xiàn)。比如:
@svg square {
@rect {
fill: var(--color, black);
width: 100%;
height: 100%;
}
}
#example {
background: white svg(square param(--color #00b1ff));
}
編譯出來就是:
#example {
background: white url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www./2000/svg'%3E%3Crect fill='%2300b1ff' width='100%25' height='100%25'/%3E%3C/svg%3E");
}
這個方案簡單易用,是我所需要的。目前測試下來,基本能達(dá)到我所需要的需求。但有一點(diǎn)千萬別忘了,記得在<head> 中添加:
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no" />
上面闡述的是這個適配方案中所用到的技術(shù)點(diǎn),簡單的總結(jié)一下:
- 使用
vw 來實(shí)現(xiàn)頁面的適配,并且通過PostCSS的插件postcss-px-to-viewport把px 轉(zhuǎn)換成vw 。這樣的好處是,我們在擼碼的時候,不需要進(jìn)行任何的計(jì)算,你只需要根據(jù)設(shè)計(jì)圖寫px 單位
- 為了更好的實(shí)現(xiàn)長寬比,特別是針對于
img 、vedio 和iframe 元素,通過PostCSS插件postcss-aspect-ratio-mini來實(shí)現(xiàn),在實(shí)際使用中,只需要把對應(yīng)的寬和高寫進(jìn)去即可
- 為了解決
1px 的問題,使用PostCSS插件postcss-write-svg,自動生成border-image 或者background-image 的圖片
這里使用了多個PostCSS的插件,其實(shí)現(xiàn)在有很多優(yōu)秀的PostCSS插件能幫助我們解決很多問題。哪果你從未接觸過有關(guān)于PostCSS相關(guān)的知識,建議你可以花點(diǎn)時間去學(xué)習(xí)一下,在W3cplus提供了一些有關(guān)于PostCSS相關(guān)的文章。如果你想系統(tǒng)的學(xué)習(xí)PostCSS相關(guān)的知識,推薦你購買《深入PostCSS Web設(shè)計(jì)》一書:

降級處理
最開始提到過,到目前為止,T30的機(jī)型中還有幾款機(jī)型是不支持vw 的適配方案。那么如果業(yè)務(wù)需要,應(yīng)該怎么處理呢?有兩種方式可以進(jìn)行降級處理:
Viewport不足之處
采用vw 來做適配處理并不是只有好處沒有任何缺點(diǎn)。有一些細(xì)節(jié)之處還是存在一定的缺陷的。比如當(dāng)容器使用vw 單位,margin 采用px 單位時,很容易造成整體寬度超過100vw ,從而影響布局效果。對于類似這樣的現(xiàn)象,我們可以采用相關(guān)的技術(shù)進(jìn)行規(guī)避。比如將margin 換成padding ,并且配合box-sizing 。只不過這不是最佳方案,隨著將來瀏覽器或者應(yīng)用自身的Webview對calc() 函數(shù)的支持之后,碰到vw 和px 混合使用的時候,可以結(jié)合calc() 函數(shù)一起使用,這樣就可以完美的解決。
另外一點(diǎn),px 轉(zhuǎn)換成vw 單位,多少還會存在一定的像素差,畢竟很多時候無法完全整除。
到目前為止,我發(fā)現(xiàn)的兩個不足之處?;蛟S在后面的使用當(dāng)中,還會碰到一些其他不為人之的坑。事實(shí)也是如此,不管任何方案,踩得坑越多,該方案也越來越強(qiáng)大。希望喜歡這個適配方案的同學(xué)和我一起踩坑,讓其更為完善。
如何判斷自己的應(yīng)用是否支持
雖然該文的示例,進(jìn)行了多方面的測試。但很多同學(xué)還是會擔(dān)憂自己的APP應(yīng)用是否支持該方案,而不敢大膽嘗試或者使用。其實(shí)不必要這么擔(dān)心,你可以拿自己的設(shè)備,或者應(yīng)用掃描下面的二維碼:
當(dāng)頁面跑完測試之后,找到對應(yīng)的Values and Units列表項(xiàng):

如果vw 欄是綠色代表你的設(shè)備或應(yīng)用支持該方案;反之則不支持。另外你也可以經(jīng)常關(guān)注css3test相關(guān)的更新,后面將會根據(jù)相關(guān)的規(guī)范更新測試代碼,讓你能快速掌握哪些屬性可以大膽使用。
總結(jié)
H5頁面的適配方案總是令人蛋疼的,事實(shí)上頁面的布局總是令人蛋疼的。但技術(shù)是不斷革新的,我們可以隨著保持對新技術(shù)的關(guān)注,嘗試這些新特性運(yùn)用到實(shí)際項(xiàng)目中,只有這樣,我們解決問題的方案才會越來越完善。
到寫這篇文章為止,雖然還有那么一兩款機(jī)型不支持vw ,但并不影響我們?nèi)ナ褂?。只有不斷去嘗試,才會有進(jìn)步。在此,希望大家大膽嘗試,一起讓該方案變得更完美。如果你有更好的建議,或者你踩到任何坑,歡迎在下面的評論中與我分享,或者發(fā)郵件給我一起討論。
 常用昵稱“大漠”,W3CPlus創(chuàng)始人,目前就職于手淘。對HTML5、CSS3和Sass等前端腳本語言有非常深入的認(rèn)識和豐富的實(shí)踐經(jīng)驗(yàn),尤其專注對CSS3的研究,是國內(nèi)最早研究和使用CSS3技術(shù)的一批人。CSS3、Sass和Drupal中國布道者。2014年出版《 圖解CSS3:核心技術(shù)與案例實(shí)戰(zhàn)》。
如需轉(zhuǎn)載,煩請注明出處:https://www./css/vw-for-layout.html
|