【第1143期】优化移动端window.onscroll的执行频率方案

来源:互联网 发布:java手机编译器aide 编辑:程序博客网 时间:2024/06/05 21:58

前言

幸亏有自动化工具,让“工作”更省时间了。今日早读文章由新浪移动@敖爽分享。

正文从这开始~

脑洞爆炸的背景

最近发现在移动端 很多时候页面滑动的速度快的时候 , 动效呈现的不稳定性越明显 , 会不流畅; 虽然使用css3的过渡可以从视觉层面解决这个问题 , 但是并不能根治, 于是乎想到了一个方案。。。

requestAnimationFrame(RAF)

h5新增的用于刷帧的api , 大家可以网上找到很多相关教程 , 用法及其简单 , 跟使用setTimeOut一样; 此api的初衷本人理解为用于更好的执行动画 , 而找到的一句话 “执行渲染下一帧之前的动作”可能更好的帮助你理解这个api;

方案

而之前说的移动端动画不流畅的原因是因为快速滑动的时候 , 两次出发scroll之间的“间距”越来越大,而导致需要根据滑动计算的精度越来越不准 , 我们当然希望每滑动1px执行一次scroll是最完美的啦~(虽然基本不可能)

于是乎 , 想到了一个方案?!

可以在window.scroll开始的时候开启RAF,在window.scroll结束的时候关闭RAF , 所有需要执行在scroll中的函数搬到RAF中执行就好了

事实上实验结果是成功的

每次window.scroll的时候在页面插入一次scroll字样 , 每次raf执行的时候插入raf字样 , 在保证一段scroll过程中只存在唯一一个RAF , 输出如上图

事实证明 ios微信环境下 , raf触发的频率在快速滑动页面的时候确实高于scroll;

实现

唯一的一个实现难点在于 scrollend如何监听

在每一次scroll的时候 , 开启一个50ms的定时器 , 定时器认定为scroll结束 , 但是每次滑动都创建定时器就乱套了 , 所以要在创建定时器之前先清除定时器;

捋一下:

第一次scroll, 清除一个不存在的定时器 , 然后创建定时器 , 50ms之后执行的就是scroll结束

第二次scroll , 清除第一次创建的定时器 , 创建一个定时器 , 50ms之后执行的就是scroll结束

。。。。

最后一次scroll , 清除倒数第二次创建的定时器 , 创建一个定时器 , 由于没有下一次scroll了 , 那么这个定时器就真的是最后一次scroll了

于是通过这样的方案迂回形成了scrollEnd , 虽然有50ms的误差~

然后代码如下 :

var rAF = window.requestAnimationFrame ||
window
.webkitRequestAnimationFrame ||
window
.mozRequestAnimationFrame ||
window
.oRequestAnimationFrame ||
window
.msRequestAnimationFrame ||
function (callback) { window.setTimeout(callback, 1000 / 60); };
var cancelRAF = window.cancelAnimationFrame ||
window
.webkitCancelAnimationFrame ||
window
.webkitCancelRequestAnimationFrame ||
window
.mozCancelRequestAnimationFrame ||
window
.oCancelRequestAnimationFrame ||
window
.msCancelRequestAnimationFrame ||
clearTimeout
;
class BetterScroll {
constructor
() {
   
let sy = window.scrollY;
   
this.onScroll = this.onScroll;
   
this.onScrollEnd = this.onScrollEnd;
   
this.scrollList = [];
   
this.scrollEndList = [];
   
this.scrollTimer = null;
   
this.nowWsy = sy;
   
this.lastY  =  sy;
   
this.direction = 0;
   
this.rafMark = null;
   
this.rafingMark = false;
   
this.gap = 0;
   
this.bindEvent();
}
onScroll
(cb) {
   
if (typeof cb !== 'function') {
       
return;
   
}
   
this.scrollList.push(cb);
}
onScrollEnd
(cb) {
   
if (typeof cb !== 'function') {
       
return;
   
}
   
this.scrollEndList.push(cb);
}
scrollEnd
() {
   
let winInfo = {
       sy
: this.nowWsy,
       gap
: Math.abs(this.gap),
       dir
: this.direction,
   
}
   
for (let i = 0, len = this.scrollEndList.length; i < len; i++) {
       
try {
           
this.scrollEndList[i](winInfo);
       
} catch (error) {
           console
.warn(error)
       
}
   
}
}
rafing
() {
   
this.nowWsy = window.scrollY;
   
this.gap = this.nowWsy - this.lastY;
   
// 1为向上滑动 -1 为向下滑动
   
!!this.gap && (this.direction = (((this.gap >= 0) | 0 ) - 0.5) * 2);
   
this.lastY = this.nowWsy;
   
let winInfo = {
       sy
: this.nowWsy, //当前window的scrollY
       gap
: Math.abs(this.gap), //上次到这次滑动的距离
       dir
: this.direction,  // 滑动方向
   
}
   
for (let i = 0, len = this.scrollList.length; i < len; i++) {
       
try {
           
this.scrollList[i](winInfo);
       
} catch (error) {
           console
.warn(error)
       
}
   
}
   
this.startRaf();
}
startRaf
() {
   
let _this = this;
   
this.rafMark = rAF(function () {
       _this
.rafing();
   
})
}
bindEvent
() {
   
let _this = this;
   window
.addEventListener('scroll', function () {
       clearTimeout
(_this.scrollTimer);
       
if (!_this.rafingMark) {
           _this
.startRaf();
           _this
.rafingMark = true;
       
}
       _this
.scrollTimer = setTimeout(function () {
           cancelRAF
(_this.rafMark);
           _this
.scrollEnd();
           _this
.rafingMark = false;
       
}, 50);
   
}, 0)
}
}
let btScroll = new BetterScroll();
export default btScroll;

用法 :

组件抛出btScroll对象 ,

提供两个方法

window正在scrolling的函数,回调函数接受参数

btScroll.onScroll(callback); 

window滑动结束的函数,回调函数接受参数winInfo

winInfobtScroll.onScrollEnd(callback);

最后:

winInfo:{
   sy
:windowscrollY值,
   gap
:上一次scroll到这一次scroll之间的差值绝对值,
   dir
:window的滑动方向1为浏览器滚动条向下滚动,-1为浏览器滚动条向上滚动
}

最后,为你推荐

【图书】指尖上行 移动前端开发进阶之路(彩印)

【第635期】百度移动端首页前端速度那些事儿

关于本文
作者:@敖爽
原文:https://zhuanlan.zhihu.com/p/32061260

寻找用kindle的你。。。

【早读Live】迅雷前端敏捷开发之路


原创粉丝点击