瀏覽器高性能滑動解決方案

vinsonli 8年前發布 | 9K 次閱讀 高性能 前端技術

最近一段時間,在項目中實現過瀑布流、懶加載、側邊欄導航等功能。總覺得在處理瀏覽器scroll滑動的時候有點問題,通過計算各個模塊的高度和某些指定模塊的出現時機時,在PC端還好,在移動端容易出現卡頓和抖動的情況。特此整理解決方案。

在停止滑動后執行

如果我們需要在滑動的時候進行某些操作,可以在停止滑動后再延遲進行,這樣就不會一邊滑動一邊執行了。

//滑動停止后延遲wait毫秒后才執行func
function debounce(func, wait) {
    // 定時器變量
    var timeout;
    return function() {
        // 每次觸發 scroll handler 時先清除定時器
        clearTimeout(timeout);
        // 指定 xx ms 后觸發真正想進行的操作 handler
        timeout = setTimeout(func, wait);
    };
};

// 實際想綁定在 scroll 事件上的處理函數
function realFunc(){
    console.log("Success");
}

// 采用了防抖動
window.addEventListener('scroll',debounce(realFunc,500));
// 沒采用防抖動
//window.addEventListener('scroll',realFunc);

還可以更好的封裝一番:

// 防抖動函數
function debounce(func, wait, immediate) {
    var timeout;
    return function() {
        var context = this, args = arguments;
        var later = function() {
            timeout = null;
            if (!immediate) func.apply(context, args);
        };
        var callNow = immediate & !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
    };
};

var myEfficientFn = debounce(function() {
    // 滾動中的真正的操作
}, 250);

// 綁定監聽
window.addEventListener('scroll', myEfficientFn);

節流,在滑動中間隔執行

如果我們不是在滑動停止后執行,而是在滑動中需要實時計算一些東西,就可以采用 節流 的方式。

節流函數,只允許一個函數在 X 毫秒內執行一次。

// 簡單的節流函數
function throttle(func, wait, mustRun) {
    var timeout,
        startTime = new Date();

    return function() {
        var context = this,
            args = arguments,
            curTime = new Date();

        clearTimeout(timeout);
        // 如果達到了規定的觸發時間間隔,觸發 handler
        if(curTime - startTime >= mustRun){
            func.apply(context,args);
            startTime = curTime;
        // 沒達到觸發間隔,重新設定定時器
        }else{
            timeout = setTimeout(func, wait);
        }
    };
};
// 實際想綁定在 scroll 事件上的 handler
function realFunc(){
    console.log("Success");
}
// 采用了節流函數
window.addEventListener('scroll',throttle(realFunc,500,1000));

如果在一段時間內 scroll 觸發的間隔一直短于 500ms ,那么能保證事件我們希望調用的 handler 至少在 1000ms 內會觸發一次。

高精度控制

上面的兩種方式都是通過setTimeout來實現的,精度不夠高,如果對瀏覽器兼容性要求不高,或者是移動端web,可以使用原生的 requestAnimationFrame 來實現。

該方法的原理: 在瀏覽器的頁面重繪之前,通知瀏覽器調用一個指定的函數。該方法被調用的頻率為每秒60次 。

所以說用該方法來觸發滾動事件,相當于上面的:

throttle(func, xx, 1000/60) //xx 代表 xx ms內不會重復觸發事件 handler
var ticking = false; // rAF 觸發鎖

function onScroll(){
  if(!ticking) {
    requestAnimationFrame(realFunc);
    ticking = true;
  }
}

function realFunc(){
    // do something...
    console.log("Success");
    ticking = false;
}
// 滾動事件監聽
window.addEventListener('scroll', onScroll, false);

這樣瀏覽器就會以16.7ms的頻率觸發事件。

至于移動端,最好還是使用iscroll這樣的模擬事件滑動的庫來解決其延遲問題吧。

 

來自: http://brizer.github.io/2016/06/11/瀏覽器高性能滑動解決方案/

 

 本文由用戶 vinsonli 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!