背景
如今流行的各類移動端滾動加載、上拉刷新組件,滑動愈來愈流暢,體驗很好;
例如 better-scroll、vue-infinite-scroll、iscroll、vue-scroll 等等比較好的方案去解決業務上的問題,但是筆者總會聽到使用過程當中也會產生一些奇怪的問題,或許會引發不少人的共鳴
好比不少 better-scroll 小白會在一開始發現不能滾動,或者滾動加載異步數據better-scroll頁面怎麼不能滾動css
滾動加載了重複的數據(分頁)、
明明下面有數據,卻拉不動等等
各類姿式不對。
筆者曾經已入坑了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
對於 vue-infinite-scroll 這一類是經過原生onscroll 事件,判斷scrollTop到達底部觸發loadMore 加載異步數據, 固然原生的scroll 的缺點是
滾動點透,好比說在body上有一個彈窗滾動,滾動到底部以後會發現body 在滾了;第二個問題是,在finger觸摸滾動而未結束滾動時,若是要作一些動做會有延時,不能像touchMove、touchEnd 那麼靈敏android
我也是看了這篇文章獲得了啓發ios
知識點1:移動web滾動問題
在移動端,使用滾動來處理業務邏輯的狀況有不少,例如列表的滾動加載數據,下拉刷新等等都須要利用滾動的相關知識,可是滾動事件在不一樣的移動端機型卻又有不一樣的表現,下面就來一一總結一下。
滾動事件:即onscroll事件,造成緣由通俗解釋是當子元素的高度超過父元素的高度時且父元素的高度時定值window除外,就會造成滾動條,滾動分爲兩種:局部滾動和body滾動。
onscroll方法: 通常狀況下當咱們須要監聽一個滾動事件時一般會用到onscroll方法來監聽滾動事件的觸發。
若是在瀏覽器上調試這個方法在瀏覽器上很好用,可是若是跑在手機端就沒有想象中的效果了。
body滾動:在移動端若是使用body滾動,意思就是頁面的高度由內容自動撐大,body天然造成滾動條,這時咱們監聽window.onscroll,發現onscroll並無實時觸發,只在手指觸摸的屏幕上一直滑動時和滾動中止的那一刻才觸發,採用了wk內核的webview除外。git
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滾動github
分別用ios手q和微信和android手q體驗會有不一樣的結果。
知識點2:關於模擬滾動web
1). 監聽滾動元素的touchmove事件,當事件觸發時修改元素的transform屬性來實現元素的位移,讓手指離開時觸發touchend事件,而後採用requestanimationframe來在一個線型函數下不斷的修改元素的transform來實現手指離開時的一段慣性滾動距離。segmentfault
2).監聽滾動元素的touchmove事件,當事件觸發時修改元素的transform屬性來實現元素的位移,讓手指離開時觸發touchend事件,而後給元素一個css的animation,並設置好duration和function來實現手指離開時的一段慣性距離。瀏覽器
模擬滾動的fps值波動較大,這樣滾動起來會有明顯的卡頓感受,各位體驗的時候若是滾動超過10屏以後就能夠明顯感受到兩着的區別。
在使用模擬滾動時,瀏覽器在js層面會消耗更多的性能去改變dom元素的位置,在dom複雜層級深的頁面更爲高,因此在長列表滾動時還要使用正常滾動更好。
知識點3:滾動和下拉刷新
方案1:藉助iscroll的原理,整個頁面使用模擬滾動,將下拉刷新元素放在頂部,當頁面滾動到頂部下拉時,下拉刷新元素隨着頁面的滾動出現,當手指離開時收回,此方案實現起來較爲簡單直接藉助iscoll便可,可是使用了模擬滾動以後在正常的列表滾動時性能上不如正常滾動。
方案2:頁面使用正常滾動,將下拉刷新元素放置在頂部top值爲負值(正常狀況下不可見),當頁面處於頂部時下拉,這時監聽touchmove事件,修改scrollcontent的tranlateY值,同時修改下拉刷新元素的tranlateY值,將二者同時位移來將下拉刷新元素顯示出來,手指離開時(touchend)收回,這種方案知足了在正常列表滾動時使用原生的滾動節省性能,只在下拉刷新時使用模擬滾動來實現效果。
方案3:方案2的改良版,惟一不一樣是將下拉刷新元素和scrollcontent放在一個div裏,將下拉刷新元素的margintop設爲負值,在下拉刷新時,只須要修改scrollcontent一個元素的tranlateY值便可實現下拉,在性能上要比方案2好。
1) 列表較長時dom數量較多時,在觸發下拉刷新的時機時將頁面視窗以外的dom元素隱藏或者存放在fragment裏面。
2) 在刷新完成以後手指離開(touchend)時將隱藏的元素顯示出來。
3) 須要注意的是,隱藏和顯示視窗外的元素這個操做在下拉刷新時只會執行一次,而且只有在下拉刷新時纔會執行。
看完這篇文章,會感受 better-scroll 也沒那麼牛逼了
本身的見解
滾動加載、上拉刷新一直是個前端界內一直在解決的題目,最近愈加感受本身要有本身的滾動插件,一個是用別人的東西不順手,有問題只能在百度碰運氣,解決不了問題可能要換框架或者改源碼,時間也是挺耗的
我仍是喜歡 原生的onscroll 滾動加載,本身如今也是用這個實現的滾動加載,屢試不爽
在大神面前慚愧地貼下代碼
進入頁面時 document.addEventListener("scroll", this.isToBottom);
離開頁面時 document.removeEventListener("scroll", this.isToBottom);
敬請期待
如今筆者正開發一個用兩百行代碼就能夠實現原生onscroll滾動加載、下拉刷新,可以兩次點擊回到頂部、盡力打造穩定、解決問題、兼容性好、入手快、維護簡單、源碼簡易的一個插件,區別於better-scroll 這種流暢感較好的插件,也算是一個 符合大衆的一個解決方案,沒有最好的只有最適合本身的