主流移動滾動外掛分析以及思路方案拓展(原生onscroll 事件實現的滾動載入不遜色)
背景
現在流行的各種移動端滾動載入、上拉重新整理元件,滑動越來越流暢,體驗很好;
例如 better-scroll、vue-infinite-scroll、iscroll、vue-scroll 等等比較好的方案去解決業務上的問題,可是筆者總會聽到使用過程中也會產生一些奇怪的問題,或許會引起很多人的共鳴
比如很多 better-scroll 小白會在一開始發現不能滾動,或者滾動載入非同步資料 ofollow,noindex" target="_blank">better-scroll頁面怎麼不能滾動
滾動載入了重複的資料(分頁)、
明明下面有資料,卻 拉不動 等等
各種 姿勢 不對。
筆者曾經已入坑了vue-infinite-scroll 和vue-scroll
vue-infinite-scroll是利用原生scroll 滾動,優點是原生滾動可以在列表載入了很多的時候不會卡頓,( 黑科技 )ios在微信上點兩下回到頂部,但是不能滑動加速,外掛本身不支援下拉重新整理,而且在v-if 的元件下用到滾動比如一個 側邊欄 ,會 報錯 $mount error , 發現原因是我元件還是隱藏的時候,外掛預設給我去跑$mount 鉤子 ,我是通過改原始碼解決的, 好坑 ,不過官方還是很快地發現了這個bug, 所以我覺得仍然是 比較靠譜的
vue-scroller坑比較多,現在好像已經好久沒維護了,不知道大家有無遇到過, 在同一個頁面用vue-scroll 然後彈窗要 textarea 輸入,超過行數產生滾動條,但 坑爹 的是textarea 竟然 不能滾動了 ,看原始碼發現是touchMove 的事件裡禁止了原生滾動,提了 連結issue ,我只能手動改原始碼了, 有圖有真相
對於滾動外掛的實現原理以及優缺點
對better-scroll、vue-scroll 這類滾動外掛是要父元素container 定位在body, 禁止原生滾動,然後通過touch 事件, 改變transform: translateY去實現 ,然後 refresh 去更新模擬滾動條長度
對於 vue-infinite-scroll 這一類是通過原生onscroll 事件,判斷scrollTop到達底部觸發loadMore 載入非同步資料
知識點1:移動web滾動問題
在移動端,使用滾動來處理業務邏輯的情況有很多,例如列表的滾動載入資料,下拉重新整理等等都需要利用滾動的相關知識,但是滾動事件在不同的移動端機型卻又有不同的表現,下面就來一一總結一下。
滾動事件:即onscroll事件,形成原因通俗解釋是當子元素的高度超過父元素的高度時且父元素的高度時定值window除外,就會形成滾動條,滾動分為兩種:區域性滾動和body滾動。
onscroll方法: 一般情況下當我們需要監聽一個滾動事件時通常會用到onscroll方法來監聽滾動事件的觸發。
如果在瀏覽器上除錯這個方法在瀏覽器上很好用,但是如果跑在手機端就沒有想象中的效果了。
body滾動:在移動端如果使用body滾動,意思就是頁面的高度由內容自動撐大,body自然形成滾動條,這時我們監聽window.onscroll,發現onscroll並沒有實時觸發,只在手指觸控的螢幕上一直滑動時和滾動停止的那一刻才觸發,採用了wk核心的webview除外。
body滾動
區域性滾動
區域性滾動:在移動端如果使用區域性滾動,意思就是我們的滾動在一個固定寬高的div內觸發,將該div設定成overflow:scroll/auto;來形成div內部的滾動,這時我們監聽div的onscroll發現觸發的時機區分android和ios兩種情況,具體可以看下面表格:
不同機型onscroll事件觸發情況:
body滾動 區域性滾動
ios 不能實時觸發 不能實時觸發
android 實時觸發 實時觸發
ios wkwebview核心 實時觸發 實時觸發
wkwebview核心:這裡說明一下關於ios的wkwebview核心是ios從ios8開始提供的新型webview核心,和之前的uiwebview相比,效能要好,具體大家可以自行檢視關於wkwebview的相關概念。
body滾動和區域性滾動demo:這裡我需要指出的是在採用wkwebview核心的頁面中scroll是可以實時觸發的,如果使用的是原本的uiwebview則不能夠實時觸發,手q目前使用的是uiwebview而新版微信使用的是wkwebview,大家可以分別使用來嘗試一下下面的demo:
區域性滾動
body滾動
分別用ios手q和微信和android手q體驗會有不同的結果。
知識點2:關於模擬滾動
- 有了上面介紹的關於滾動的知識,理解的模擬滾動就不難了。
- 正常的滾動:我們平時使用的scroll,包括上面講的滾動都屬於正常滾動,利用瀏覽器自身提供的滾動條來實現滾動,底層是由瀏覽器核心控制。
- 模擬滾動:最典型的例子就是iscroll了,原理一般有兩種:
1). 監聽滾動元素的touchmove事件,當事件觸發時修改元素的transform屬性來實現元素的位移,讓手指離開時觸發touchend事件,然後採用requestanimationframe來在一個線型函式下不斷的修改元素的transform來實現手指離開時的一段慣性滾動距離。
2).監聽滾動元素的touchmove事件,當事件觸發時修改元素的transform屬性來實現元素的位移,讓手指離開時觸發touchend事件,然後給元素一個css的animation,並設定好duration和function來實現手指離開時的一段慣性距離。
- 這兩種方案對比起來各有好處,第一種方案由於慣性滾動的時機時由js自己控制所以可以拿到滾動觸發階段的scrolltop值,並且滾動的回撥函式onscroll在滾動的階段都會觸發。
- 第二種方案相比第一種要劣勢一些,區別在於手指離開時,採用的時css的animation來實現慣性滾動,所以無法直接觸發慣性滾動過程中的onscroll事件,只有在animation結束時才可以藉助animationend來獲取到事件,當然也有一種方法可以實時獲取滾動事件,也是藉助於requestanimationframe來不斷的去讀取滾動元素的transform來拿到scrolltop同時觸發onscroll回撥。
模擬滾動的fps值波動較大,這樣滾動起來會有明顯的卡頓感覺,各位體驗的時候如果滾動超過10屏之後就可以明顯感覺到兩著的區別。
在使用模擬滾動時,瀏覽器在js層面會消耗更多的效能去改變dom元素的位置,在dom複雜層級深的頁面更為高,所以在長列表滾動時還要使用正常滾動更好。
知識點3:滾動和下拉重新整理
- 下拉重新整理的元素在頁面頂部,正常瀏覽時不可見的。
- 當在頁面頂部往下滾動時出現下拉重新整理元素,當手指離開時收起。
- 以上兩點時實現一個下拉重新整理元件的基本步驟,結合我們上述關於滾動的描述,我們可以這樣實現下拉重新整理:
方案1:藉助iscroll的原理,整個頁面使用模擬滾動,將下拉重新整理元素放在頂部,當頁面滾動到頂部下拉時,下拉重新整理元素隨著頁面的滾動出現,當手指離開時收回,此方案實現起來較為簡單直接藉助iscoll即可,但是使用了模擬滾動之後在正常的列表滾動時效能上不如正常滾動。
方案2:頁面使用正常滾動,將下拉重新整理元素放置在頂部top值為負值(正常情況下不可見),當頁面處於頂部時下拉,這時監聽touchmove事件,修改scrollcontent的tranlateY值,同時修改下拉重新整理元素的tranlateY值,將兩者同時位移來將下拉重新整理元素顯示出來,手指離開時(touchend)收回,這種方案滿足了在正常列表滾動時使用原生的滾動節省效能,只在下拉重新整理時使用模擬滾動來實現效果。
方案3:方案2的改良版,唯一不同是將下拉重新整理元素和scrollcontent放在一個div裡,將下拉重新整理元素的margintop設為負值,在下拉重新整理時,只需要修改scrollcontent一個元素的tranlateY值即可實現下拉,在效能上要比方案2好。
- 在採用了上述方案之後,還會有一個性能上的問題就是:當頁面的列表過長,dom元素過多時,在模擬滾動,下拉重新整理這段時間內,頁面也會有卡頓現象,這裡採取了一個優化策略即:
1) 列表較長時dom數量較多時,在觸發下拉重新整理的時機時將頁面視窗之外的dom元素隱藏或者存放在fragment裡面。
2) 在重新整理完成之後手指離開(touchend)時將隱藏的元素顯示出來。
3) 需要注意的是,隱藏和顯示視窗外的元素這個操作在下拉重新整理時只會執行一次,並且只有在下拉重新整理時才會執行。
看完這篇文章,會感覺 better-scroll 也沒那麼 牛逼 了
自己的看法
滾動載入、上拉重新整理一直是個前端界內一直在解決的題目,最近越發感覺自己要有自己的滾動外掛,一個是用別人的東西不順手,有問題只能在百度碰運氣,解決不了問題可能要換框架或者改原始碼,時間也是挺耗的
我還是喜歡 原生的onscroll 滾動載入,自己現在也是用這個實現的滾動載入, 屢試不爽
在大神面前慚愧地貼下程式碼
進入頁面時 document.addEventListener("scroll", this.isToBottom);
離開頁面時 document.removeEventListener("scroll", this.isToBottom);
敬請期待
現在筆者正開發一個用兩百行程式碼就可以實現原生onscroll滾動載入、下拉重新整理,能夠兩次點選回到頂部、盡力打造穩定、解決問題、相容性好、入手快、維護簡單、原始碼簡易的一個外掛,區別於better-scroll 這種流暢感較好的外掛,也算是一個 符合大眾的一個解決方案, 沒有最好的只有最適合自己的