背景
如今流行的各类移动端滚动加载、上拉刷新组件,滑动愈来愈流畅,体验很好;
例如 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 这种流畅感较好的插件,也算是一个 符合大众的一个解决方案,没有最好的只有最适合本身的