像圖片一樣佈局

移動端經常會有這樣的展示頁面,頁面中有一些獨立的元素,並且伴隨各種交互效果、轉場動畫。

img img

針對頁面佈局的話,大概有這樣的需求:

  • 不滾動頁面,所有元素均在一屏之類展示
  • 像圖片那樣縮放元素---寬度改變時高度按比例變化
  • 背景圖需要把瀏覽器窗口撐滿
  • 往往項目是獨立的,需要快速上線,開發週期短

這種形式頁面在正常的文檔流中佈局顯然不太合適,比較傳統的做法是這樣:

  • 佈局以絕對定位爲主
  • 用百分比配合px控制寬高、位置
  • 背景圖用background-size:cover撐滿
  • 用css媒體查詢來適應多種屏幕
  • css搞不定的請js幫忙

佈局一個單屏展示頁

比如這樣一個640*960的設計稿,分背景和按鈕兩層,要求背景全屏(可以不顯示全)按鈕定位在精確的位置上。

img

傳統做法

一般會把背景圖用background-size:cover顯示在一個寬高撐滿屏幕的容器上以適應各種屏幕, 這個按鈕怎麼定位呢,一般是會這樣做吧position:absolute;bottom:18%;

模擬一下iPhone5微信中的顯示
img

iPhone4微信中的顯示
img

可以看到iPhone4中按鈕錯位,原因是當屏幕尺寸改變時,背景圖與按鈕的尺寸與位置會遵循不同的規則變化。這個例子中的效果貌似還能接受,可有些場景對定位的要求可能比這更高。

解決這個問題一般會用媒體查詢針對屏幕做適配吧

@media screen and (max-height: 500px) {
  .btn{
    bottom:12%;
  }
}

Demo 在這裏 http://jsbin.com/zugape/edit

如果頁面中元素多、定位要求精確,這種做法是比較暴力的。下面是支付寶十年曬單頁面中針對不同尺寸屏幕做的適配

/* <=640 */
@media screen and (min-width: 319px) and (max-width: 321px) {
    ...
}
/* 720 */
@media screen and (min-width: 359px) and (max-width: 361px) {
    ...
}
/* 1024*768, 2056*1536 */
@media screen and (min-width: 383px) and (max-width: 385px) {
    ...
}
/* 800 */
@media screen and (min-width: 399px) and (max-width: 401px) {
    ...
}
/* 1080 */
@media screen and (min-width: 539px) and (max-width: 541px) {
    ...
}
/* 1200 */
@media screen and (min-width: 599px) and (max-width: 601px) {
    ...
}
/* 1440 */
@media screen and (min-width: 719px) and (max-width: 721px) {
    ...
}
/* 1600 */
@media screen and (min-width: 799px) and (max-width: 801px) {
    ...
}
/* for small screen */
@media screen and (max-height: 500px) {
    ...
}
/* for iPhone6 Plus */
@media only screen and (device-width: 414px) and (-webkit-min-device-pixel-ratio: 3),
only screen and (device-width: 375px) and (-webkit-min-device-pixel-ratio: 3){
    ...
}

總結下這種方式的缺點:

  • 要把設計稿中的PX在大腦中換算成%
  • 百分比控制寬高是相對於父元素,計算繁瑣
  • 用媒體查詢針對各種尺寸的屏幕做兼容工作繁瑣

圖片的自適應規則

一張圖片如果給他設置<img src="pic" style="max-width: 100%;max-height:100%” />的話,無論視口大小,他都會顯示在視口裏面,如圖
img img

類似的還有圖片作爲背景顯示時的background-size屬性。
background-size: contain:縮放圖像的最大值,其寬度和高度都能放入內容區域
img

background-size: cover:縮放圖像的最小值,其寬度和高度都能放入內容區域
img

當你改變圖片的寬度或高度時,圖片的另一邊會自動按比例縮放,如果div有這種能力多好啊!

vw、vh--爲移動而生

vw、vh是CSS3中出現的新的長度單位,vw 相對於視窗的寬度,視窗寬度是100vw;vh 相對於視窗的高度,視窗高度是100vh。
其中視窗指的是瀏覽器實際顯示區域,即window.innerWidth/window.innerHeight的大小。
如 想一個元素的寬是視口寬的一半,只需設置width:50vw

與百分比的異同

乍一看好像和百分比類似,並沒發現多少優勢,在進行一些嘗試之後我總結了一下他們的異同

百分比 vw、vh
相對父級元素 相對視口
橫向/縱向相對父級橫向/縱向 vw/vh相對視口寬/高(即寫vh就是相對視口高、寫vw就是相對視口寬,不必橫向與橫向縱向與縱向對應)
對字體大小無效 對字體大小有效

Tips:其實margin-top/bottom以百分比爲單位時他的參照對象並不是父元素的height而是width。詳情閱讀 doyoe margin系列文章

這些特性可以實現一些之前用CSS難以解決的問題。

相對視口100%高度

由於百分比高度是相對父級的,之前如果想讓文檔流中的某容器相對視口高度100%,只用css的話得從html>body>element一層層下來都設置height:100%,而現在只需給元素設置height:100vh
height 100% Demo http://jsbin.com/risegu/edit

響應式文字 && 像圖片一樣佈局

先上Demo
百分比方式 http://jsbin.com/vemudo/edit
vw方式 http://jsbin.com/huwido/edit 
這兩個Demo建議把瀏覽器模擬成移動設備的樣子,拉伸瀏覽器窗口寬度觀察效果。

img img

可以看到當瀏覽器寬度改變時,百分比Demo中只有元素的寬度發生了變化,而統一使用vw做單位的Demo中文字的大小、元素的高度與寬度均按縮放比例發生了變化,這正是我們想要的“像圖片一樣佈局”的效果。

vmin 與 vmax

還有vw、vh延生出來的單位vmin、vmax。
vmin:關於視口高度和寬度兩者的最小值
vmax:關於視口高度和寬度兩者的最大值

我覺得這是一組神奇的單位,但是暫時對這個使用場景的理解有限,能想到的場景是一個正方形,height:100vmin;width:100vmin時,得到的是這個視口中能顯示滿的一個最大正方形(http://jsbin.com/baheko);height:100vmax;width:100vmax時得到的是能把這個視口全部顯示滿的最小正方形(http://jsbin.com/tamasi)。

然後在codepen上找到這樣一個demo,他讓16:9的視頻窗口在視口中自適應http://codepen.io/CreativeJuiz/pen/KzkgL

其他適用場景有待發掘

vw、vh的問題

vw vh 在桌面端瀏覽器兼容性已經是比較好了,不過在移動端貌似還需要等待,國產安卓瀏覽器的話UC到現在都不支持真不讓人省心。
img

另外對張鑫旭的視區相關單位vw, vh..簡介以及可實際應用場景 中他的結論:vw vh只適用於非定位元素;vh高度值的內部元素不支持百分比%高度 有些疑問,我在測試中並未發現這樣的情況,這篇文章寫於2012年,不知道是不是這樣的情況只發生在當時的瀏覽器。Demo http://jsbin.com/dineka/edit

rem方案

回頭來看我們的佈局,vw vh的方案因爲兼容性問題只能放棄,重新分析下我們的需求:
其實我們需要那麼一個全局的相對單位...
我們可以通過修改這個單位來控制整個佈局...
我們可以通過判斷屏幕的比例來縮放整個頁面以展示全部內容...

然後想到類似手機淘寶的動態rem的方案:所有佈局元素單位使用rem,由於rem是相對於根元素的,所以不同屏幕按一定規則控制的font-size 即可兼容各屏幕。

var root = document.getElementsByTagName('html')[0],
    NATIVE_W = 640;
function updateSize() {
    var w = window.innerWidth;
    var cw = w / (NATIVE_W / 100);  
    root.style.fontSize = cw + 'px';
}
window.onload = updateSize;
window.onresize = updateSize;

這樣的頁面一般會有設計稿的,我們需要設置rem和設計稿中px的轉換比例。
1rem = 1px 這樣固然方便,但是有些瀏覽器有最小字體的限制。
所以我想讓 1rem = 100px。
我們的設計稿一般爲640 x 960,這個尺寸是針對iPhone4,設備像素比是2,那麼我們實際需要html的font-size爲50px。
這樣的話假如設計稿中一個元素尺寸爲240px x 200px,
那麼這個元素需要在屏寬爲320px的設備上顯示的尺寸是120px x 100px,而我們需要他在佈局時設置的寬高是2.4rem x 2rem。
設計稿寬640px 也就是整個頁面應爲 6.4rem。 所以對應不同寬度的設備時用window.innerWidth(視口寬) / 6.4 得到的就是對應此設備的font-size。
佈局時,把設計稿上的尺寸除以100然後把px單位換成rem。

怎麼解決比較短的屏幕顯示不完全的情況呢,可以先判斷一下視口的比例,當視口高寬比低於設計稿寬高比時,縮小html節點的font-size這樣就能把內容顯示全了

var root = document.getElementsByTagName('html')[0],
    NATIVE_W = 640,
    NATIVE_H = 960;

function htmlSize() {
    var cw = 50,
        w = window.innerWidth,
        h = window.innerHeight;  
    if ((w / h) > (NATIVE_W / NATIVE_H)) {
        cw = h / (NATIVE_H / 100);
    } else {
        cw = w / (NATIVE_W / 100);
    }
    root.style.fontSize = cw + 'px';
}
window.onload = htmlSize;
window.onresize = htmlSize;

Demo http://jsbin.com/rupole/edit
可以試下縮小、拉大窗口布局也會像圖片那樣相應變化,並且無論窗口是什麼樣的尺寸,元素都會出現在比較合理的位置,當然,一些特殊情況仍然要用媒體查詢來適配。

然後在佈局時,其實只要把這段js放在頁面中,就rem當成px來用就行了,不需要多考慮啥,這個方案的優點就是構建頁面成本低,跨屏顯示效果也能滿意。

calc

另外有些元素我們想他垂直方向上針對中心定位而不是頂部或底部,比如想讓一個小球居中之前大概是這樣:

#cir{
  width:200px;
  height:200px;
  border-radius:100px;
  background:#888;
  position:absolute;
  left:50%;
  top:50%;
  margin-left:-100px;
  margin-top:-100px;
}

其實CSS3中有這樣一個表達式:calc()(CSS里居然有這玩意,我第一次看到時懷疑是不是SASS或LESS裏的),他可以給元素的border、margin、pading、font-size和width等屬性動態計算值,例如:top:calc(50% - 1rem),這在這種展示頁面的佈局中是相當有用的,上面的css可以改寫成:

#cir{
  width:200px;
  height:200px;
  border-radius:100px;
  background:#888;
  position:absolute;
  left:calc(50% - 100px);
  top:calc(50% - 100px);
}

Demo http://jsbin.com/gasato/edit

關於更多calc的介紹建議閱讀大漠的這篇博文 http://www.w3cplus.com/css3/how-to-use-css3-calc-function.html

遺憾的是這玩意在桌面端瀏覽器中支持的已經比較好了,不過在移動端的表現令人堪憂,安卓原生瀏覽器從4.4開始支持,而最新版的安卓UC仍然不支持。

img

原文地址:https://github.com/xiangpaopao/blog/issues/9

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章