前景提要
HDC調試需求開發(15萬預算),能者速來!>>> 問題背景 : 一個WebApp要做的像個App,多半會需要一個固定位置的頭部、工具欄之類的效果,多半你會想到position:fixed;然后發現 哎呀 ,iOS的Safari上position:fixed;居然無效
解決問題前先來看看問題是怎樣產生的: Apple是這樣解釋的 Safari on iPad and Safari on iPhone do not have resizable windows. In Safari on iPhone and iPad, the window size is set to the size of the screen (minus Safari user interface controls), and cannot be changed by the user. To move around a webpage, the user changes the zoom level and position of the viewport as they double tap or pinch to zoom in or out, or by touching and dragging to pan the page. As a user changes the zoom level and position of the viewport they are doing so within a viewable content area of fixed size (that is, the window). This means that webpage elements that have their position “fixed” to the viewport can end up outside the viewable content area, offscreen. 這里有個關鍵的東西叫做viewport,你經常在頁面的頭部里可以見到它: <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1.0, user-scalable=no" /> 想起來了吧,就是它讓你的頁面不會像在桌面上那樣顯示,玩過windows的放大鏡功能吧, 你可以把viewport想象成一個類似的放大鏡,fixed的元素是相對整個頁面固定位置的,你在屏幕上滑動只是在移動這個所謂的viewport,原來的網頁還好好的在那,fixed的內容也沒有變過位置,所以說并不是iOS不支持fixed,只是fixed的元素不是相對手機屏幕固定的。 換個角度,如果所有fixed的元素都相對屏幕固定,那那些桌面版的網頁在手機上還能看嗎。
替代方案 弄清楚原因,接下來就是如何解決了,這里我參考了比較有代表性的兩種解決方案。
jQuery Mobile 之前用這個框架做東西的時候就覺得它對fixed的處理很奇怪(demo),理解了上述viewport的模型后會覺得他的想法很簡單也很自然,既然fixed的元素不會隨viewport移動而移動,那就用相對定位讓其隨著viewport的移動改變位置,通過webkit的touch事件我們可以很方便的獲取當前手指位置的坐標,據此計算偏移量并改變fixed元素的top偏移量,可以模擬元素fix在viewport的效果。盡管touchmove事件可以讓你隨時獲得當前偏移量,理論上可以做到平滑的滾動,但由于手指觸發事件到瀏覽器完成渲染是需要時間的,如此模擬出的效果必定做不到理想中的fixed效果,更重要的是touchend事件后的scroll階段,在這個階段iOS會將DOM操作掛起(其API的ScrollEvent部分有說明),即會造成頁面無法即時渲染。于是jQueryMobile就決定在整個viewport移動過程中(包括touch與scroll)讓fixed的元素消失,scroll事件結束后再fadeOut??傊褪?,很 奇 怪。
Sencha Touch/TWITTER Twitter的處理方式則創新得多,效果也更加完美(后來發現Sencha Touch也是這樣處理的),因為這個方案實現了真·fixed。所謂真·fixed,是因為在手指移動的過程中,不管是fixed元素的位置還是viewport的位置都沒有改變。具體做法是監聽body的touchmove事件,獲取偏移量以改變內容元素的位置,并用event.preventDefault()阻止瀏覽器的默認scroll動作,來看代碼:<div class="tw-scrollview-host" style="min-height: 1018px; -webkit-transform: translate3d(0px, -22px, 0px);">....</div> 這里用translate3d只是因為僅有這個CSS屬性iOS是調用硬件加速的…. 但是由于其阻止了瀏覽器的默認滾動,所以當touch事件結束后內容是不會慣性滾動的,于是又需要繼續改變偏移量來模擬Scroll事件,這里就涉及到Scroll算法的問題了,要考慮手指移動的速度、阻尼的大小跟邊界情況等等,我沒有找到這部分的代碼,也沒有搜到任何相關文章,如果有人了解可以分享一下。 總的來說,Twitter Sencha Touch的方法在效果上更流暢,也更符合一般人的心理模型,如果硬要說缺點的話,我覺得這種實現太“不原生”了,幾乎完全拋棄了移動瀏覽器的viewport模型,大興土木地用CSS3與js模擬的scroll在執行效率與效果上與原生的差別也有待考究。
有個好消息: iOS5即將更好的支持position:fixed;屬性,加上android早在2.2就已經實現,以后要實現類似效果就不用再這么折騰了。
更好的消息:
不管用jQuery.mobile還是Sencha Touch ,這兩框架都太重了,針對于國內的3G網絡,下載框架+暄染頁面都得等老半天,后來,我找著了了iScroll,它的實現原理跟Sencha Touch一樣,效果流暢,還不卡,再后來,我用iScroll做了手機站: http://mm.lewole.com ,把平時喜歡的冷笑話和內涵段子收集在了一起~~~等公車,坐地鐵時看一看,無聊時樂一樂~~~