優化移動端window.onscroll的執行頻率方案

腦洞爆炸的背景

最近開發項目動效開發愈來愈多 ;
部分動效須要在頁面滑動的時候執行必定的效果;
可是發如今移動端 不少時候頁面滑動的速度快的時候 , 動效呈現的不穩定性越明顯 , 會不流暢; 雖然使用css3的過渡能夠從視覺層面解決這個問題 , 可是並不能根治, 因而乎想到了一個方案。。。css

requestAnimationFrame(RAF)

h5新增的用於刷幀的api , 你們能夠網上找到不少相關教程 , 用法及其簡單 , 跟使用setTimeOut同樣; 此api的初衷本人理解爲用於更好的執行動畫 , 而找到的一句話 「執行渲染下一幀以前的動做」可能更好的幫助你理解這個api;ios

方案

而以前說的移動端動畫不流暢的緣由是由於快速滑動的時候 , 兩次出發scroll之間的「間距」愈來愈大,而致使須要根據滑動計算的精度愈來愈不許 , 咱們固然但願每滑動1px執行一次scroll是最完美的啦~(雖然基本不可能)css3

因而乎 , 想到了一個方案?!web

能夠在window.scroll開始的時候開啓RAF,在window.scroll結束的時候關閉RAF , 全部須要執行在scroll中的函數搬到RAF中執行就行了 api

事實上實驗結果是成功的瀏覽器

圖片描述

每次window.scroll的時候在頁面插入一次scroll字樣 , 每次raf執行的時候插入raf字樣 , 在保證一段scroll過程當中只存在惟一一個RAF , 輸出如上圖微信

事實證實 ios微信環境下 , raf觸發的頻率在快速滑動頁面的時候確實高於scroll;函數

實現

惟一的一個實現難點在於 scrollend如何監聽動畫

在每一次scroll的時候 , 開啓一個50ms的定時器 , 定時器認定爲scroll結束 , 可是每次滑動都建立定時器就亂套了 , 因此要在建立定時器以前先清除定時器;this

捋一下:

第一次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對象 ,

提供兩個方法

btScroll.onScroll(callback);  window正在scrolling的函數 , 回調函數接受參數 winInfo

btScroll.onScrollEnd(callback); window滑動結束的函數 , 回調函數接受參數 winInfo


winInfo  :  {
    sy : window的scrollY值,
    gap : 上一次scroll到這一次scroll之間的差值絕對值,
    dir : window的滑動方向 1爲瀏覽器滾動條向下滾動 , -1爲瀏覽器滾動條向上滾動,
}

歡迎各位大大交流 , 有更好的腦洞和哪裏寫的不足的地方歡迎留言討論!

相關文章
相關標籤/搜索