解決頁面使用overflow: scroll在iOS上滑動卡頓的問題

最近的一次開發中,使用到了overflow:scroll 屬性來滑動div。
如果你對某個div或模塊使用了overflow: scroll屬性,在iOS系統的手機上瀏覽時,則會出現明顯的卡頓現象。
但是在android系統的手機上則不會出現該問題。大家不妨可以分別使用IOS和Android系統的手機瀏覽以下鏈接,滑動文字區域查看該效果(重點是記住iPhone瀏覽時的效果,方便瀏覽后文):http://geek100.com/demo/os.html.

以下代碼可解決這種卡頓的問題:-webkit-overflow-scrolling: touch;,是因為這行代碼啟用了硬件加速特性,所以滑動很流暢。

實際上,Safari真的用了原生控件來實現,對于有-webkit-overflow-scrolling的網頁,會創建一個UIScrollView,提供子layer給渲染模塊使用。

在WebKit 108400版本左右才支持,所以iOS Safari應該是需要5.0。Android則是在4.0以上支持。
從前端開發的角度講,只需要知道CSS的屬性-webkit-overflow-scrolling是真的創建了帶有硬件加速的系統級控件,所以效率很高。

上述所說的方法的確可以解決ios5.0、android4.0以后系統的滑動卡頓問題,不過呢在這還可以為大家推薦一些相關插件:iScroll

https://github.com/cubiq/iscroll

IScroll 實踐指南

之所以iscroll會誕生,主要是因為無論是在iphone、ipod、android 或是更早前的移動webkit都沒有提供一種原生的方式來支持在一個固定高度的容器內滾動內容。
這個不幸的規則導致所有web-app要模擬成app的樣子時,只能由一個絕對定位的header 或是footer再加上一個可以內容的滾動的中間區域組成。
幸運的是移動webkit提供了一種強大的硬件加速的CSS屬性,這個屬性可以用來模擬這個缺失的功能,Iscroll從這里開始了前進之路,但是沒有不帶刺的玫瑰。讓內容滾動像原生方式一般比想象中要難

通過樣式:

overflow:scroll;  
-webkit-overflow-scrolling:touch;  

IOS5 已經能夠支持區域滾動了。但是andriod4 還是不行...

iScroll 使用起來很簡單,首先你需要一個合理的DOM結構:

<div id="wrapper">  
    <ul id="scroll">  
        <li></li>  
        ...  
        ...  
    </ul>  
</div>  

推薦的樣式:

#wrapper {  
    position:relative;  
    z-index:1;  
    width:/* your desired width, auto and 100% are fine */;  
    height:/* element height */;  
    overflow:/* hidden|auto|scroll */;  
}  

官方推薦這樣的結構,因為iscroll只能滾動wrapper里的第一個子節點,或者說唯一一個子節點才能使得iscroll正確的生效。因為這個節點需要一個絕對定位的CSS屬性,更重要的是這個節點里所包裹的內容有了一個統一的容器,我們只需要計算之后修改這個容器的屬性值就可以達到我們滾動的效果。

iscroll 需要兩個參數,第一個很簡單就是外容器的id,第二個參數是一個參數對象。
通過這個對象可以傳入iscroll的各項參數來配置iscroll。
他的參數基本分為四個部分

  • 基礎
  • 滾動條
  • 放大縮小
  • 事件回調

以下是 iScroll參數以及其代表的意思:

hScroll: true, //是否水平滾動  
vScroll: true, //是否垂直滾動  
x: 0, //滾動水平初始位置  
y: 0, //滾動垂直初始位置  
bounce: true, //是否超過實際位置反彈  
bounceLock: false, //當內容少于滾動是否可以反彈,這個實際用處不大  
momentum: true, //動量效果,拖動慣性  
lockDirection: true,  
//當水平滾動和垂直滾動同時生效時,當拖動開始是否鎖定另一邊的拖動  
useTransform: true, //是否使用CSS形變  
useTransition: false, //是否使用CSS變換  
topOffset: 0, //已經滾動的基準值(一般情況用不到)  
checkDOMChanges: false, //是否自動檢測內容變化  

checkDOMChanges 這個不是十分靠得住,因為他目前是輪詢檢測offsetWidthoffsetHeight,然后才去調自身的refresh 重新計算滾動區域,但是有時候只檢測這個不是很準..

// Scrollbar 的相關參數  
hScrollbar: true, //是否顯示水平滾動條  
vScrollbar: true, //同上垂直滾動條  
fixedScrollbar: isAndroid, //對andriod的fixed  
hideScrollbar: isIDevice,  //是否隱藏滾動條  
fadeScrollbar: isIDevice && has3d, //滾動條是否漸隱漸顯  
scrollbarClass: '', //字定義滾動條的樣式名  

通過scrollbar這些參數可以配置iscroll的滾動條,通過scrollbarClass可以自己定義一套滾動條的樣式。

// Zoom 放大相關的參數  
zoom: false, //默認是否放大  
zoomMin: 1, //放大的最小倍數  
zoomMax: 4, //最大倍數  
doubleTapZoom: 2, //雙觸放大幾倍  
wheelAction: 'scroll', //鼠標滾動行為(還可以是zoom)  

這個Zoom我覺得比較好用,對于一個固定顯示圖片區域的類似應用,可以非常簡單的做到固定滾動,包括兩指放大的應用。
wheelAction 這個參數是給PC的鼠標滾動定義的,可以定義為滾動鼠標滾輪放大。

// 自定義 Events 的相關參數   
onRefresh: null, //refresh 的回調,關于自身何時調用refresh 后面會繼續談到  
onBeforeScrollStart: function (e) { e.preventDefault(); },   
//開始滾動前的時間回調,默認是阻止瀏覽器默認行為  
onScrollStart: null, //開始滾動的回調  
onBeforeScrollMove: null, //在內容移動前的回調  
onScrollMove: null, //內容移動的回調  
onBeforeScrollEnd: null, 在滾動結束前的回調  
onScrollEnd: null, //在滾動完成后的回調  
onTouchEnd: null, //手離開屏幕后的回調  
onDestroy: null, //銷毀實例的回調  
onZoomStart: null,  
onZoom: null,   
onZoomEnd: null  

通過了解以上參數,你可以非常容易的配置自己的iscroll 應用:
你可以通過onScrollEnd 事件回調在結束滾動后執行一段你自己的代碼
你也可以簡單的新建一個可以通過雙觸放大的固定滾動區域。
你也可以什么都不做,只是簡單的約定碰到邊界是否反彈,等等。

var myscroll = new iScroll('wrapper', {  
     hScroll: false, //是否水平滾動  
     vScroll: true, //是否垂直滾動  
     y: 10, //滾動垂直初始位置  
     bounce : false  
});  

當然,在使用時,如果對創建的iscroll 實例保存引用會有很多好處:
你可以在內容改變時,DOM結構發生改變時調用 myscroll.refresh()來重新計算固定滾動區域的內容高度,從而使得你的iscroll工作正常。
你也可以在你的應用結束時,用過這個引用調用destroy方法來,銷毀這個iscroll 實例
等等....
當然,iscroll提供的Api也是非常豐富,所以我們可以通過使用iscroll來做很多webapp的應用

下面介紹一下iscroll的公用調用方法,以及參數的控制。

如何使用 iscroll 提供的API,以及一些沒有提供的功能,如何通過參數來控制iscroll
Iscroll 提供的調用方法有:

destroy
refresh
scrollTo
scrollToElement
scrollToPage
disable
enable
stop
zoom
isReady

destroy
顧名思義,是用來銷毀你實例化的iScroll 實例,包括之前綁定的所有iscroll 事件。

refresh
這個方法非常有用,當你的滾動區域的內容發生改變 或是 滾動區域不正確,都用通過調用refresh來使得iscroll 重新計算滾動的區域,包括滾動條,來使得iscroll 適合當前的dom。

scrollTo
這個方法接受4個參數x, y, time, relative x為移動的x軸坐標,y為移動的y軸坐標, time為移動時間,relative表示是否相對當前位置。

scrollToElement
這個方法實際上是對scrollTo的進一步封裝,接受兩個參數(el,time),el為需要滾動到的元素引用,time為滾動時間。

scrollToPage
此方法接受三個參數(pageX,pageY,time)當滾動內容的高寬大于滾動范圍時,iscroll 會自動分頁,然后就能使用scrollToPage方法滾動到頁面。當然,當hscrollfalse 的時候,不能左右滾動。pageX這個參數就失去效果

disable
調用這個方法會立即停止動畫滾動,并且把滾動位置還原成0,取消綁定touchmove,touchendtouchcancel事件。

enable
調用這個方法,使得iscroll恢復默認正常狀態

stop
立即停止動畫

zoom
改變內容的大小倍數,此方法接受4個參數,x,y,scale,time分別表示的意思為,放大的基準坐標,以及放大倍數,動畫時間

isReady
當iscroll 沒有處于正在滾動,沒有移動過,沒有改變大小時,此值為true

上一篇沒有談到snap這個屬性,而這個屬性往往是需要用iscroll作滾動組件非常關鍵的一個屬性。還記得我們的iphone 菜單滾動效果吧?當手指觸摸屏幕向左拉動到一半的情況,應用菜單會自動滾動對齊到下一頁。這個snap 屬性就是用來實現這種效果的。

<script type="text/javascript">  
var myScroll;  
  
function loaded() {  
    myScroll = new iScroll('wrapper', {  
        snap: 'li',  
        momentum: false,  
        hScrollbar: false,  
        vScrollbar: false  
     });  
}  
  
document.addEventListener('DOMContentLoaded', loaded, false);  
</script>  

以上是官方例子的代碼,展示了iscroll 滑動對齊到元素li。

snap值可以為true 或是 DOM元素的tagname,當為true時,對齊的坐標會根據可滾動的位置和滾動區域計算得到可滑動幾頁。如果為tagname,則滑動會對齊到元素上。舉個例子
假設有這樣一個列表,每個li里的img 都為居中顯示,maxWidth 都等于屏幕寬度,li的寬度都為屏幕的寬度,那么上面的代碼就可以實現一個滾動圖片組件了

<ul>  
    <li>  
       ![](img.jpg)  
    <li>  
    <li>  
       ![](img.jpg)  
    <li>  
    <li>  
       ![](img.jpg)  
    <li>  
    <li>  
       ![](img.jpg)  
    <li>  
    </ul>  

我們看到 iscroll 的所有的屬性和 api 都旨在做一件事情,就是在固定區域內滾動。當然通過snap,我們可以很好的模擬iphone 菜單間的平滑滾動。

轉載請注明原文出處:http://qbaty.iteye.com/blog/1221061

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 問答題47 /72 常見瀏覽器兼容性問題與解決方案? 參考答案 (1)瀏覽器兼容問題一:不同瀏覽器的標簽默認的外補...
    _Yfling閱讀 13,814評論 1 92
  • 文、攝影:垂楊紫陌 冬來了 大地越發地性感 先褪去了青澀 再褪去成熟 再褪去整夜的喧囂和浮華 冷眼看時間裸奔著前進...
    love垂楊紫陌閱讀 193評論 2 2
  • 靚穎這幾天上頭條了,事情大家看新聞也都知道。今天和友人吃飯,連她這樣除了工作基本不問世事的人都聽說了這個八卦...
    猴得柱閱讀 258評論 0 0
  • 其實一點兒都不奇怪,因為我愛你,我知道。 不是萬家燈火掩住了所有的星光 不是一望無際的海都沉默在荒涼 我都不會說愛...
    擼串兒評論員閱讀 225評論 0 0
  • 斯坎倫保護區(Scanlon Creek Conservation Area)位于多倫多北面大約1小時車程的小鎮B...
    田園讀書人閱讀 664評論 0 4