互聯(lián)網(wǎng)上的自適應(yīng)方案到底有幾種呢?就我個(gè)人實(shí)踐所知,有這么幾種方案:
-
固定一個(gè)某些寬度,使用一個(gè)模式,加上少許的媒體查詢(xún)方案
-
使用flexbox解決方案
-
使用百分比加媒體查詢(xún)
-
使用rem
淘寶最近開(kāi)源的一個(gè)框架和網(wǎng)易的框架有同工之異。都是采用rem實(shí)現(xiàn)一稿解決所有設(shè)置自適應(yīng)。在沒(méi)出來(lái)這種方案之前,第一種做法的人數(shù)也不少。類(lèi)似以下說(shuō)到的拉鉤網(wǎng)??匆幌铝髟浦T葛的文章。
以下摘自:從網(wǎng)易與淘寶的font-size思考前端設(shè)計(jì)稿與工作流
1. 簡(jiǎn)單問(wèn)題簡(jiǎn)單解決
我覺(jué)得有些web app并一定很復(fù)雜,比如拉勾網(wǎng),你看看它的頁(yè)面在iphone4,iphone6,ipad下的樣子就知道了:
它的頁(yè)面有一個(gè)特點(diǎn),就是:
-
頂部與底部的bar不管分辨率怎么變,它的高度和位置都不變
-
中間每條招聘信息不管分辨率怎么變,招聘公司的圖標(biāo)等信息都位于條目的左邊,薪資都位于右邊
這種app是一種典型的彈性布局:關(guān)鍵元素高寬和位置都不變,只有容器元素在做伸縮變換。對(duì)于這類(lèi)app,記住一個(gè)開(kāi)發(fā)原則就好:文字流式,控件彈性,圖片等比縮放。以圖描述:
這個(gè)規(guī)則是一套基本的適配規(guī)則,對(duì)于這種簡(jiǎn)單app來(lái)說(shuō)已經(jīng)足夠,同時(shí)它也是后面要說(shuō)的rem布局的基礎(chǔ)。另外對(duì)于拉勾這種app可能需要額外媒介查詢(xún)對(duì)布局進(jìn)行調(diào)整的就是小屏幕設(shè)備。舉例來(lái)說(shuō),因?yàn)楝F(xiàn)在很多設(shè)計(jì)稿是根據(jù)iphone6的尺寸來(lái)的,而iphon6設(shè)備寬的邏輯的像素是375px,而iphone4的邏輯像素是320個(gè)像素,所以如果你根據(jù)設(shè)計(jì)稿做出來(lái)的東西,在iphone4里面可能顯示不下,比如說(shuō)拉鉤網(wǎng)底部那個(gè)下載框,你對(duì)比看下就知道了,這是4:
這是6:
6下面兩邊的間距比4多很多,說(shuō)明拉勾對(duì)4肯定是做過(guò)適配的,從代碼也可以證實(shí)這一點(diǎn):
不過(guò)如果你拿到的是根據(jù)4的設(shè)計(jì)稿,那就沒(méi)有問(wèn)題,比4分辨率大的設(shè)備肯定能顯示根據(jù)4的尺寸做出來(lái)的東西。
還有一點(diǎn),這種情況css尺寸單位用px就好,不要用rem,避免增加復(fù)雜度。
2. 網(wǎng)易的做法
先來(lái)看看網(wǎng)易在不同分辨率下,呈現(xiàn)的效果:
從上面幾張圖可以看出,隨著分辨率的增大,頁(yè)面的效果會(huì)發(fā)生明顯變化,主要體現(xiàn)在各個(gè)元素的寬高與間距。375*680的比320*680的導(dǎo)航欄明顯要高。能夠達(dá)到這種效果的根本原因就是因?yàn)榫W(wǎng)易頁(yè)面里除了font-size之外的其它c(diǎn)ss尺寸都使用了rem作為單位,比如你看導(dǎo)航欄的高度設(shè)置代碼:
可是在本文第1部分提到,使用rem布局結(jié)合在html上根據(jù)不同分辨率設(shè)置不同font-size有很多不好解決的麻煩,網(wǎng)易是如何解決的呢?最根本的原因在于,網(wǎng)易頁(yè)面上html的font-size不是預(yù)先通過(guò)媒介查詢(xún)?cè)赾ss里定義好的,而是通過(guò)JS計(jì)算出來(lái)的,所以當(dāng)分辨率發(fā)生變化時(shí),html的font-size就會(huì)變,不過(guò)這得在你調(diào)整分辨率后,刷新頁(yè)面才能看得到效果。你看代碼就知道為啥font-size是直接寫(xiě)到html的style上面的了(js設(shè)置的原因):
它是根據(jù)什么計(jì)算的,這就跟設(shè)計(jì)稿有關(guān)了,拿網(wǎng)易來(lái)說(shuō),它的設(shè)計(jì)稿應(yīng)該是基于iphone4或者iphone5來(lái)的,所以它的設(shè)計(jì)稿豎直放時(shí)的橫向分辨率為640px,為了計(jì)算方便,取一個(gè)100px的font-size為參照,那么body元素的寬度就可以設(shè)置為width: 6.4rem,于是html的font-size=deviceWidth / 6.4。這個(gè)deviceWidth就是viewport設(shè)置中的那個(gè)deviceWidth。根據(jù)這個(gè)計(jì)算規(guī)則,可得出本部分開(kāi)始的四張截圖中html的font-size大小如下:
deviceWidth = 320,font-size = 320 / 6.4 = 50px deviceWidth = 375,font-size = 375 / 6.4 = 58.59375px deviceWidth = 414,font-size = 414 / 6.4 = 64.6875px deviceWidth = 500,font-size = 500 / 6.4 = 78.125px
事實(shí)上網(wǎng)易就是這么干的,你看它的代碼就知道,body元素的寬是:
根據(jù)這個(gè)可以肯定它的設(shè)計(jì)稿豎著時(shí)的橫向分辨率為640。然后你再看看網(wǎng)易在分辨率為320*680,375*680,414*680,500*680時(shí),html的font-size是不是與上面計(jì)算的一致:
320*680
375*680
414*680
500*680
這個(gè)deviceWidth通過(guò)document.documentElement.clientWidth就能取到了,所以當(dāng)頁(yè)面的dom ready后,做的第一件事情就是:
document.documentElement.style.fontSize = document.documentElement.clientWidth / 6.4 + 'px';
這個(gè)6.4怎么來(lái)的,當(dāng)然是根據(jù)設(shè)計(jì)稿的橫向分辨率/100得來(lái)的。下面總結(jié)下網(wǎng)易的這種做法:
-
(1)先拿設(shè)計(jì)稿豎著的橫向分辨率除以100得到body元素的寬度:
如果設(shè)計(jì)稿基于iphone6,橫向分辨率為750,body的width為750 / 100 = 7.5rem 如果設(shè)計(jì)稿基于iphone4/5,橫向分辨率為640,body的width為640 / 100 = 6.4rem
-
(2)布局時(shí),設(shè)計(jì)圖標(biāo)注的尺寸除以100得到css中的尺寸,比如下圖:
-
-
播放器高度為210px,寫(xiě)樣式的時(shí)候css應(yīng)該這么寫(xiě):height: 2.1rem。之所以取一個(gè)100作為參照,就是為了這里計(jì)算rem的方便!
-
(3)在dom ready以后,通過(guò)以下代碼設(shè)置html的font-size:
document.documentElement.style.fontSize = document.documentElement.clientWidth / 6.4 + 'px';
-
6.4只是舉個(gè)例子,如果是750的設(shè)計(jì)稿,應(yīng)該除以7.5。
-
(4)font-size可能需要額外的媒介查詢(xún),并且font-size不能使用rem,如網(wǎng)易的設(shè)置:
@media screen and (max-width:321px){ .m-navlist{font-size:15px} } @media screen and (min-width:321px) and (max-width:400px){ .m-navlist{font-size:16px} } @media screen and (min-width:400px){ .m-navlist{font-size:18px} }
最后還有2個(gè)情況要說(shuō)明:
第一,如果采用網(wǎng)易這種做法,視口要如下設(shè)置:
<meta name="viewport" content="initial-scale=1,maximum-scale=1, minimum-scale=1">
第二,當(dāng)deviceWidth大于設(shè)計(jì)稿的橫向分辨率時(shí),html的font-size始終等于橫向分辨率/body元素寬:
640*680
641*680
之所以這么干,是因?yàn)楫?dāng)deviceWidth大于640時(shí),則物理分辨率大于1280(這就看設(shè)備的devicePixelRatio這個(gè)值了),應(yīng)該去訪(fǎng)問(wèn)pc網(wǎng)站了。事實(shí)就是這樣,你從手機(jī)訪(fǎng)問(wèn)網(wǎng)易,看到的是觸屏版的頁(yè)面,如果從pad訪(fǎng)問(wèn),看到的就是電腦版的頁(yè)面。如果你也想這么干,只要把總結(jié)中第三步的代碼稍微改一下就行了:
var deviceWidth = document.documentElement.clientWidth; if(deviceWidth > 640) deviceWidth = 640; document.documentElement.style.fontSize = deviceWidth / 6.4 + 'px';
3. 淘寶的做法
看看淘寶在不同分辨率下,呈現(xiàn)的效果:
淘寶的效果跟網(wǎng)易的效果其實(shí)是類(lèi)似的,隨著分辨率的變化,頁(yè)面元素的尺寸和間距都相應(yīng)變化,這是因?yàn)樘詫毜某叽缫彩鞘褂昧藃em的原因。在介紹它的做法之前,先來(lái)了解一點(diǎn)關(guān)于viewport的知識(shí),通常我們采用如下代碼設(shè)置viewport:
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
這樣整個(gè)網(wǎng)頁(yè)在設(shè)備內(nèi)顯示時(shí)的頁(yè)面寬度就會(huì)等于設(shè)備邏輯像素大小,也就是device-width。這個(gè)device-width的計(jì)算公式為:
設(shè)備的物理分辨率/(devicePixelRatio * scale),在scale為1的情況下,device-width = 設(shè)備的物理分辨率/devicePixelRatio 。
devicePixelRatio稱(chēng)為設(shè)備像素比,每款設(shè)備的devicePixelRatio都是已知,并且不變的,目前高清屏,普遍都是2,不過(guò)還有更高的,比如2.5, 3 等,我魅族note的手機(jī)的devicePixelRatio就是3。淘寶觸屏版布局的前提就是viewport的scale根據(jù)devicePixelRatio動(dòng)態(tài)設(shè)置:
在devicePixelRatio為2的時(shí)候,scale為0.5
在devicePixelRatio為3的時(shí)候,scale為0.3333
這么做目的當(dāng)然是為了保證頁(yè)面的大小與設(shè)計(jì)稿保持一致了,比如設(shè)計(jì)稿如果是750的橫向分辨率,那么實(shí)際頁(yè)面的device-width,以iphone6來(lái)說(shuō),也等于750,這樣的話(huà)設(shè)計(jì)稿上標(biāo)注的尺寸只要除以某一個(gè)值就能夠轉(zhuǎn)換為rem了。通過(guò)js設(shè)置viewport的方法如下:
var scale = 1 / devicePixelRatio; document.querySelector('meta[name="viewport"]').setAttribute('content','initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
淘寶布局的第二個(gè)要點(diǎn),就是html元素的font-size的計(jì)算公式,font-size = deviceWidth / 10:
接下來(lái)要解決的問(wèn)題是,元素的尺寸該如何計(jì)算,比如說(shuō)設(shè)計(jì)稿上某一個(gè)元素的寬為150px,換算成rem應(yīng)該怎么算呢?這個(gè)值等于設(shè)計(jì)稿標(biāo)注尺寸/該設(shè)計(jì)稿對(duì)應(yīng)的html的font-size。拿淘寶來(lái)說(shuō)的,他們用的設(shè)計(jì)稿是750的,所以html的font-size就是75,如果某個(gè)元素時(shí)150px的寬,換算成rem就是150 / 75 = 2rem??偨Y(jié)下淘寶的這些做法:
-
(1)動(dòng)態(tài)設(shè)置viewport的scale
var scale = 1 / devicePixelRatio; document.querySelector('meta[name="viewport"]').setAttribute('content','initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
-
(2)動(dòng)態(tài)計(jì)算html的font-size
document.documentElement.style.fontSize = document.documentElement.clientWidth / 10 + 'px';
-
(3)布局的時(shí)候,各元素的css尺寸=設(shè)計(jì)稿標(biāo)注尺寸/設(shè)計(jì)稿橫向分辨率/10
-
(4)font-size可能需要額外的媒介查詢(xún),并且font-size不使用rem,這一點(diǎn)跟網(wǎng)易是一樣的。
最后還有一個(gè)情況要說(shuō)明,跟網(wǎng)易一樣,淘寶也設(shè)置了一個(gè)臨界點(diǎn),當(dāng)設(shè)備豎著時(shí)橫向物理分辨率大于1080時(shí),html的font-size就不會(huì)變化了,原因也是一樣的,分辨率已經(jīng)可以去訪(fǎng)問(wèn)電腦版頁(yè)面了。
關(guān)于這種做法的具體實(shí)現(xiàn),淘寶已經(jīng)給我們提供了一個(gè)開(kāi)源的解決方案,具體請(qǐng)查看:
https://github.com/amfe/lib-flexible
之前沒(méi)有找到這相關(guān)的資料,實(shí)在不好意思:(
4. 比較網(wǎng)易與淘寶的做法
共同點(diǎn):
-
都能適配所有的手機(jī)設(shè)備,對(duì)于pad,網(wǎng)易與淘寶都會(huì)跳轉(zhuǎn)到pc頁(yè)面,不再使用觸屏版的頁(yè)面
-
都需要?jiǎng)討B(tài)設(shè)置html的font-size
-
布局時(shí)各元素的尺寸值都是根據(jù)設(shè)計(jì)稿標(biāo)注的尺寸計(jì)算出來(lái),由于html的font-size是動(dòng)態(tài)調(diào)整的,所以能夠做到不同分辨率下頁(yè)面布局呈現(xiàn)等比變化
-
容器元素的font-size都不用rem,需要額外地對(duì)font-size做媒介查詢(xún)
-
都能應(yīng)用于尺寸不同的設(shè)計(jì)稿,只要按以上總結(jié)的方法去用就可以了
不同點(diǎn)
-
淘寶的設(shè)計(jì)稿是基于750的橫向分辨率,網(wǎng)易的設(shè)計(jì)稿是基于640的橫向分辨率,還要強(qiáng)調(diào)的是,雖然設(shè)計(jì)稿不同,但是最終的結(jié)果是一致的,設(shè)計(jì)稿的尺寸一個(gè)公司設(shè)計(jì)人員的工作標(biāo)準(zhǔn),每個(gè)公司不一樣而已
-
淘寶還需要?jiǎng)討B(tài)設(shè)置viewport的scale,網(wǎng)易不用
-
最重要的區(qū)別就是:網(wǎng)易的做法,rem值很好計(jì)算,淘寶的做法肯定得用計(jì)算器才能用好了 。不過(guò)要是你使用了less和sass這樣的css處理器,就好辦多了,以淘寶跟less舉例,我們可以這樣編寫(xiě)less:
//定義一個(gè)變量和一個(gè)mixin
@baseFontSize: 75;//基于視覺(jué)稿橫屏尺寸/100得出的基準(zhǔn)font-size .px2rem(@name, @px){ @{name}: @px / @baseFontSize * 1rem; }
//使用示例:
.container { .px2rem(height, 240); }
//less翻譯結(jié)果: .container { height: 3.2rem; }
5. 如何與設(shè)計(jì)協(xié)作
前端與設(shè)計(jì)師的協(xié)作應(yīng)該是比較簡(jiǎn)單的,最重要的是要規(guī)范設(shè)計(jì)提供給你的產(chǎn)物,通常對(duì)于前端來(lái)說(shuō),我們需要設(shè)計(jì)師提供標(biāo)注尺寸后的設(shè)計(jì)稿以及各種元素的切圖文件,有了這些就可以開(kāi)始布局了。考慮到Retina顯示屏以及這么多移動(dòng)設(shè)備分辨率卻不一樣的問(wèn)題,那么設(shè)計(jì)師應(yīng)該提供多套設(shè)計(jì)稿嗎?從網(wǎng)易和淘寶的做法來(lái)看,應(yīng)該是不用了,我們可以按照設(shè)計(jì)稿,先做出一套布局,按照以上方法做適配,由于是等比適配,所以各個(gè)設(shè)備的視覺(jué)效果差異應(yīng)該會(huì)很小,當(dāng)然也排除不了一些需要媒介查詢(xún)特殊處理的情況,這肯定避免不了的。下面這張圖是淘寶設(shè)計(jì)師分享的他們的工作流程:
解釋一下就是:
第一步,視覺(jué)設(shè)計(jì)階段,設(shè)計(jì)師按寬度750px(iphone 6)做設(shè)計(jì)稿,除圖片外所有設(shè)計(jì)元素用矢量路徑來(lái)做。設(shè)計(jì)定稿后在750px的設(shè)計(jì)稿上做標(biāo)注,輸出標(biāo)注圖。同時(shí)等比放大1.5倍生成寬度1125px的設(shè)計(jì)稿,在1125px的稿子里切圖。
第二步,輸出兩個(gè)交付物給開(kāi)發(fā)工程師:一個(gè)是程序用到的@3x切圖資源,另一個(gè)是寬度750px的設(shè)計(jì)標(biāo)注圖。
第三步,開(kāi)發(fā)工程師拿到750px標(biāo)注圖和@3x切圖資源,完成iPhone 6(375pt)的界面開(kāi)發(fā)。此階段不能用固定寬度的方式開(kāi)發(fā)界面,得用自動(dòng)布局(auto layout),方便后續(xù)適配到其它尺寸。
第四步,適配調(diào)試階段,基于iPhone 6的界面效果,分別向上向下調(diào)試iPhone 6 plus(414pt)和iPhone 5S及以下(320pt)的界面效果。由此完成大中小三屏適配。
注意第三步,就要使用我們以上介紹的網(wǎng)易跟淘寶的適配方法了。假如公司設(shè)計(jì)稿不是基于750的怎么辦,其實(shí)很簡(jiǎn)單,按上圖做一些相應(yīng)替換即可,但是流程和方法還是一樣的。解釋一下為什么要在@3x的圖里切,這是因?yàn)楝F(xiàn)在市面上也有不少像魅藍(lán)note這種超高清屏幕,devicePixelRatio已經(jīng)達(dá)到3了,這個(gè)切圖保證在所有設(shè)備都清晰顯示。