移動(dòng)端的webapp頁(yè)面布局教程和webapp實(shí)戰(zhàn)分析

頁(yè)面視圖

在講頁(yè)面結(jié)構(gòu)化之前需要先理解視圖的概念,視圖是單頁(yè)應(yīng)用開發(fā)中最常見的模塊,通常在一個(gè)單頁(yè)應(yīng)用中,會(huì)有多個(gè)視圖存在,每一個(gè)視圖都可以處理一部分業(yè)務(wù)功能,所有視圖的功能集就是單頁(yè)應(yīng)用所能處理業(yè)務(wù)的最大能力。下面介紹幾種單頁(yè)應(yīng)用中最常出現(xiàn)的幾種視圖。

2.1 單視圖層

三段式結(jié)構(gòu)是單視圖的一種最基本布局方式,如下圖:

structure-1

單視圖并不一定都有head或foot,所以Header、Footer使用虛線來表示。多數(shù)應(yīng)用中還會(huì)有導(dǎo)航條(Navigatior),但一般情況下導(dǎo)航條會(huì)被計(jì)算為Header或Content的一部分,而不會(huì)獨(dú)立存在。

2.2 側(cè)邊欄

側(cè)邊欄是一種特殊的視圖,在不顯示時(shí),當(dāng)前視圖是蓋在側(cè)邊欄至上的,當(dāng)它被呼出時(shí),視圖一部分滑出屏幕外,側(cè)邊欄才被顯示出來,它的高度等于頁(yè)面可視區(qū)域的高度。

顯示前:

structure-2

顯示后:

webAPP 頁(yè)面布局教程

2.3 封面圖

封面圖與側(cè)邊欄類似,也是一個(gè)特殊的視圖。封面圖一般會(huì)在頁(yè)面初始時(shí)候出現(xiàn),而后消失,消失之后就不再出現(xiàn)。它的視圖層級(jí)是最高的,并且完全覆蓋于其他頁(yè)面元素,它的高度會(huì)大于或等于可視區(qū)域的高度。

structure-4

3 多視圖布局

單頁(yè)應(yīng)用中第一個(gè)要思考的問題就是:如何實(shí)現(xiàn)多視圖的布局?通常我們會(huì)將視圖的定位設(shè)置為position:absolute,這是一種簡(jiǎn)單又實(shí)用的方法。在一個(gè)時(shí)間節(jié)點(diǎn)上,頁(yè)面可視區(qū)域只能有一個(gè)可見的當(dāng)前視圖,虛線表示其他視圖,在頁(yè)面可視區(qū)域之外不可見(display:none),如下圖:

structure-5

使用偽代碼表示:

<style type="text/css">

.view {

position: absolute;

top: 0;

left: 0;

z-index: 99;

display: none;

width: 100%;

height: 100%;

}

.current {

z-index: 100;

display: block;

}

</style>

<div></div>

<div></div>

此時(shí),我們需要思考另一個(gè)問題:如何實(shí)現(xiàn)當(dāng)前視圖的Content區(qū)域內(nèi)容滾動(dòng)?視圖的樣式高度設(shè)置為height:100%,將視圖高度設(shè)定為一屏高的目的是為了方便實(shí)現(xiàn)視圖動(dòng)畫切換的效果(視圖動(dòng)畫切換會(huì)在后面詳細(xì)的講)。但這樣做會(huì)導(dǎo)致另一個(gè)問題,高度為一屏高意味著瀏覽器滾動(dòng)條失效,無法使用瀏覽器滾動(dòng)條滾動(dòng)頁(yè)面。

3.1 基于iScroll的多視圖布局

現(xiàn)在比較流行的一種解決方案是使用iScroll組件實(shí)現(xiàn)固定區(qū)域滾動(dòng),這樣就能解決Content區(qū)域的滾動(dòng)問題,在手機(jī)搜狐的早期項(xiàng)目也是這么做的。此外,使用iScroll還額外帶來了一些好處,如:

  • Header區(qū)域能固定在頁(yè)面頂部,不會(huì)因?yàn)镃ontent區(qū)域滾動(dòng)導(dǎo)致Header被頂上去;
  • 單視圖的高度控制在一屏高,這樣有利于實(shí)現(xiàn)視圖之間的動(dòng)畫切換;

對(duì)于這種結(jié)構(gòu)的應(yīng)用,在使用視圖切換的時(shí)候就非常好做,使用CSS3的transition來完成動(dòng)畫切換,如下圖:

structure-6

使用偽代碼表示:

<style type="text/css">

.current.out {

-webkit-transition: -webkit-transform 400ms;

-webkit-transform: translate3d(-100%,0,0);

}

.next {

display: block;

-webkit-transform: translate3d(100%,0,0);

}

.next.in{

-webkit-transition: -webkit-transform 400ms;

-webkit-transform: translate3d(0,0,0);

}

</style>

<div></div>

<div></div>

視圖切換的動(dòng)畫效果可以根據(jù)業(yè)務(wù)需求定制,比如:由左向右滑動(dòng)、由右向左、由上到下、右下到上等都是可以的。在完成切換動(dòng)畫時(shí),再將next視圖的狀態(tài)設(shè)置為current,如下:

<div class="view"></div>

<div class="view current"></div>

 

下圖是項(xiàng)目中使用的一個(gè)由下往上動(dòng)畫切換效果:

structure-7

 

 

3.2 iScroll頁(yè)面結(jié)構(gòu)下的側(cè)邊欄

使用iScroll的頁(yè)面結(jié)構(gòu),無論是側(cè)邊欄還是封面圖都非常好實(shí)現(xiàn),看偽代碼:

側(cè)邊欄,默認(rèn)狀態(tài)

<style type="text/css">

.sidebar {

z-index: 50;

display: block;

width: 80%;

}

.sidebar.show + .current {

-webkit-transition: -webkit-transform 400ms;

-webkit-transform: translate3d(80%,0,0);

}

.sidebar.hide + .current {

-webkit-transition: -webkit-transform 400ms;

-webkit-transform: translate3d(0,0,0);

}

</style>

<div></div>

<div></div>

側(cè)邊欄顯示時(shí):

<div class="view sidebar show"></div>

<div class="view current"></div>

側(cè)邊欄隱藏時(shí),當(dāng)hide動(dòng)畫結(jié)束之后,移除hide樣式

<div class="view sidebar hide"></div>

<div class="view current"></div>

 

3.3 iScroll頁(yè)面結(jié)構(gòu)下的封面圖

封面圖的實(shí)現(xiàn)與側(cè)邊欄差不多。

封面圖,默認(rèn)狀態(tài)

<style type="text/css">

.cover {

z-index: 200;

display: block;

visibility: hidden;

opacity: 0;

}

.cover.show {

visibility: visible;

-webkit-transition: opacity 400ms;

opacity: 1;

}

.cover.hide {

visibility: visible;

-webkit-transition: opacity 400ms;

opacity: 0;

}

</style>

<div></div>

<div></div>

封面圖顯示時(shí)

<div class="view cover show"></div>

<div class="view current"></div>

封面圖隱藏時(shí),當(dāng)hide動(dòng)畫結(jié)束之后,移除hide樣式

<div class="view cover hide"></div>

<div class="view current"></div>

 

在項(xiàng)目中的實(shí)現(xiàn)效果:

structure-8

3.4 iScroll對(duì)內(nèi)容刷新的支持

對(duì)于Content區(qū)域的內(nèi)容刷新iScroll也有很好的支持,可以直接參見iScroll提供的例子:http://lab.cubiq.org/iscroll/examples/pull-to-refresh/

structure-9

Note:iScroll目前已經(jīng)更新到了5.0的版本,大家可以關(guān)注Github項(xiàng)目https://github.com/cubiq/iscroll/

 

4. 多視圖布局,新的探索

對(duì)于單頁(yè)應(yīng)用來說,iScroll確實(shí)是一個(gè)非常優(yōu)秀的解決方案,但是iScroll缺有一個(gè)最大的缺陷——慢,滾動(dòng)的性能與瀏覽器原生實(shí)現(xiàn)相比,在低端的移動(dòng)設(shè)備上有明顯卡頓,這點(diǎn)我在另一片博文中也提到過《移動(dòng)Web產(chǎn)品前端開發(fā)口訣——“快”》。

Note:目前有一個(gè)新的趨勢(shì),瀏覽器經(jīng)過一兩年的發(fā)展,Android下已經(jīng)優(yōu)化的相當(dāng)不錯(cuò),iScroll在一些較低端的移動(dòng)設(shè)備上,性能表現(xiàn)得比以前要好非常多,比如小米1,早期的米1還在運(yùn)行UC7.x的版本時(shí),iScroll明顯的卡,現(xiàn)在在UC9.x下,iScroll也能運(yùn)行得比較流暢了。

4.1 Fixed+原生Scroll

在此之下,我們也做了一些新的嘗試,第一嘗試就是放棄使用iScroll組件。放棄之后遇到的第一個(gè)問題,如何使Header固定位置在頂部?由此,我們使用了原生的CSS特性position:fixed,如下圖:

structure-10

Fixed在一些移動(dòng)設(shè)備瀏覽器上有兼容問題,我找到了一種能檢測(cè)瀏覽器是否支持position:fixed的方法,這個(gè)也發(fā)一篇博文《移動(dòng)Web開發(fā),4行代碼檢測(cè)瀏覽器是否支持position:fixed》,在檢測(cè)到瀏覽器不支持fixed時(shí),可以使用absolute作為替代方案,監(jiān)聽window的scroll事件,每次scroll動(dòng)作結(jié)束時(shí),重新計(jì)算一次Header的top值,將其定位到頁(yè)面頂部。

有關(guān)position:fixed的bug在另一篇博文中《移動(dòng)端web頁(yè)面使用position:fixed問題總結(jié)》也有總結(jié)。

另外強(qiáng)調(diào)一點(diǎn),不要在Fixed區(qū)域中直接使用input或textarea元素。在fixed元素中的input獲取焦點(diǎn)之后,彈出軟鍵盤會(huì)帶來很多額外的問題,如:

  • 在iOS下軟鍵盤彈出,fixed定位會(huì)出問題;
  • 在Android下軟件盤彈出,可能會(huì)導(dǎo)致輸入?yún)^(qū)域被遮擋;

點(diǎn)擊input彈出一個(gè)新視圖來完成后續(xù)輸入,是一種比較好的解決方案,下圖是一個(gè)基于iScroll的頁(yè)面結(jié)構(gòu)實(shí)現(xiàn):

structure-11

4.2 原生Scroll下的視圖切換

使用了原生Scroll之后,帶來最大的改變是視圖切換動(dòng)畫的變化。使用iScroll的頁(yè)面結(jié)構(gòu),視圖的高度固定,并且是position:absolute定位,所以非常容易做視圖切換。

換成原生Scroll之后,想使用一個(gè)比較緩和的動(dòng)畫過渡效果是非常困難的,可選的動(dòng)畫效果十分有限,經(jīng)過了很多試驗(yàn)之后,最后選擇使用淡入-淡出的動(dòng)畫效果,這是一種折中的方法。最初在完成這種動(dòng)畫實(shí)現(xiàn)的時(shí)候,編碼的方法比較簡(jiǎn)單,就是將當(dāng)前視圖淡出,下一視圖淡入,如下圖:

structure-12

后來在做了更多嘗試之后,開發(fā)出了一種兼容更強(qiáng)的淡入-淡出動(dòng)畫過渡。技術(shù)要點(diǎn)就是使用一個(gè)幕布層(mask)實(shí)現(xiàn)淡入效果,在mask完成淡入之后,再完成實(shí)際的視圖的切換,操作步驟大致如下:

  • 創(chuàng)建一個(gè)幕布層<div></div>,mask為position:absolute定位,初始為透明狀態(tài),背景設(shè)置為白色或其他顏色,并使mask蓋在當(dāng)前視圖上面;
  • mask使用transition實(shí)現(xiàn)opacity:1的動(dòng)畫過渡,當(dāng)完成動(dòng)畫時(shí),mask將會(huì)把當(dāng)前視圖完全遮住;
  • 最后,直接將當(dāng)前視圖隱藏,將下一視圖顯示既可;
  • 完成所有動(dòng)作之后,隱藏mask;

效果圖:

structure-13

4.3 原生Scroll頁(yè)面結(jié)構(gòu)下的側(cè)邊欄

側(cè)邊欄的結(jié)構(gòu)也變得復(fù)雜了一些,使用原生Scroll之后,body的高度會(huì)被內(nèi)容區(qū)域撐到很高,但側(cè)邊欄還是必須保證一屏高。所以我在側(cè)邊欄顯示時(shí),將html與body的高度控制為一屏高,這樣可以防止頁(yè)面被滾動(dòng)。使用偽代碼表示:

側(cè)邊欄,默認(rèn)狀態(tài)

<html>

<head>

<style type="text/css">

.frame {

height: 100%;

}

.sidebar {

background-color: red;

position: absolute;

z-index: 50;

width: 80%;

height: 100%;

}

.scroller {

background-color: green;

position: relative;

z-index: 100;

height: 2000px;

}

.sidebar-show body, .sidebar-hide body {

height: 100%;

}

.sidebar-show .scroller {

overflow: hidden;

height: 100%;

-webkit-transition: -webkit-transform 400ms;

-webkit-transform: translate3d(80%,0,0);

}

.sidebar-hide .scroller {

overflow: hidden;

height: 100%;

-webkit-transition: -webkit-transform 400ms;

-webkit-transform: translate3d(0,0,0);

}

</style>

</head>

<body>

<div></div>

<div></div>

</body>

</html>

側(cè)邊欄顯示時(shí),在html元素上增加一個(gè)樣式sidebar-show

<html class="frame sidebar-show" ?>

 

側(cè)邊欄隱藏時(shí),將html元素上的樣式替換成sidebar-hide,當(dāng)hide動(dòng)畫結(jié)束之后,移除hide樣式

<html class="frame sidebar-hide" >

在項(xiàng)目中的實(shí)際效果:

structure-14

另外,將側(cè)邊欄設(shè)置為position:fixed定位會(huì)是另一種實(shí)現(xiàn)思路。

4.4 原生Scroll頁(yè)面結(jié)構(gòu)下的封面圖

封面圖的實(shí)現(xiàn)與側(cè)邊欄差不多,使用偽代碼表示:

封面圖,默認(rèn)為顯示狀態(tài)

<html>

<head>

<style type="text/css">

.frame, .frame body {

height: 100%;

}

.cover {

background-color: red;

position: absolute;

z-index: 200;

width: 100%;

height: 100%;

}

.scroller {

background-color: green;

position: relative;

z-index: 100;

height: 2000px;

}

.cover-show body, .cover-hide body {

height: 100%;

}

.cover-show .scroller {

overflow: hidden;

height: 100%;

}

.cover-hide .cover {

-webkit-transition: opacity 400ms;

opacity: 0;

}

</style>

</head>

<body>

<div></div>

<div></div>

</body>

</html>

封面圖隱藏時(shí),將html元素上的樣式替換成cover-hide,當(dāng)hide動(dòng)畫結(jié)束之后,移除hide樣式

< ?html class="frame cover-hide >

 

項(xiàng)目中的應(yīng)用:

structure-15

4.5 原生Scroll頁(yè)面結(jié)構(gòu)下,內(nèi)容刷新的實(shí)現(xiàn)

一般情況下,我們會(huì)頁(yè)面底部放一個(gè)加載更多的按鈕,讓用戶點(diǎn)擊按鈕加載下一頁(yè)內(nèi)容,如下圖:

structure-16

又或者,監(jiān)聽window的scroll事件,當(dāng)頁(yè)面發(fā)生滾動(dòng)時(shí),監(jiān)測(cè)是否滾動(dòng)到頁(yè)面底部,自動(dòng)加載下一頁(yè)內(nèi)容。這兩種方式都能很好的解決加載下一頁(yè)的業(yè)務(wù)需求,但是對(duì)于加載最新或刷新的操作只能在頁(yè)面中放置一個(gè)刷新按鈕來完成業(yè)務(wù)需求。

對(duì)于Pull Up/Down Request的操作,在原生Scroll下,幾乎是無法實(shí)現(xiàn)的。但我依然希望能找到一種方法,實(shí)現(xiàn)Pull Request操作。

現(xiàn)在我正在研究一種模擬Pull操作的解決方案,已經(jīng)有了一個(gè)雛形,并實(shí)現(xiàn)了一些功能。下面這個(gè)示例中沒有使用任何的iScroll技術(shù),完全使用原生Scroll實(shí)現(xiàn)頁(yè)面滾動(dòng),并且滾動(dòng)到頁(yè)面底部后可以完成Pull Up操作,如下圖:

structure-17

這個(gè)技術(shù)的實(shí)現(xiàn)原理并不復(fù)雜,就是在頁(yè)面滾動(dòng)到底部時(shí),創(chuàng)建一個(gè)空白層,模擬Pull Up手勢(shì)拖動(dòng)頁(yè)面的效果。

structure-18

我后面會(huì)封裝成一個(gè)組件放在GitHub上分享給大家。

https://github.com/maxzhang/maxzhang.github.com/issues/8

每天更新,
全站高品質(zhì)素材免費(fèi)下載!