zoukankan      html  css  js  c++  java
  • 防抖(Debouncing)和节流(Throttling)

    onscoll防抖封装函数

    scroll 事件本身会触发页面的重新渲染,同时 scroll 事件的 handler 又会被高频度的触发, 因此事件的 handler 内部不应该有复杂操作,例如 DOM 操作就不应该放在事件处理中。

    针对此类高频度触发事件问题(例如页面 scroll ,屏幕 resize,监听用户输入等),下面介绍两种常用的解决方法,防抖和节流。

    防抖(Debouncing)

    防抖技术即是可以把多个顺序地调用合并成一次,也就是在一定时间内,规定事件被触发的次数。

    通俗一点来说,看看下面这个简化的例子:


    //
    防抖动函数 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);

    节流(Throttling)

    防抖函数确实不错,但是也存在问题,譬如图片的懒加载,我希望在下滑过程中图片不断的被加载出来,而不是只有当我停止下滑时候,图片才被加载出来。又或者下滑时候的数据的 ajax 请求加载也是同理。

    这个时候,我们希望即使页面在不断被滚动,但是滚动 handler 也可以以一定的频率被触发(譬如 250ms 触发一次),这类场景,就要用到另一种技巧,称为节流函数(throttling)。

    节流函数,只允许一个函数在 X 毫秒内执行一次。

    与防抖相比,节流函数最主要的不同在于它保证在 X 毫秒内至少执行一次我们希望触发的事件 handler。

    与防抖相比,节流函数多了一个 mustRun 属性,代表 mustRun 毫秒内,必然会触发一次 handler ,同样是利用定时器,看看简单的示例:

    // 简单的节流函数
    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 内会触发一次。

    使用 rAF(requestAnimationFrame)触发滚动事件

    上面介绍的抖动与节流实现的方式都是借助了定时器 setTimeout ,但是如果页面只需要兼容高版本浏览器或应用在移动端,又或者页面需要追求高精度的效果,那么可以使用浏览器的原生方法 rAF(requestAnimationFrame)。

    window.requestAnimationFrame() 这个方法是用来在页面重绘之前,通知浏览器调用一个指定的函数。这个方法接受一个函数为参,该函数会在重绘前调用。

    rAF 常用于 web 动画的制作,用于准确控制页面的帧刷新渲染,让动画效果更加流畅,当然它的作用不仅仅局限于动画制作,我们可以利用它的特性将它视为一个定时器。(当然它不是定时器)

    通常来说,rAF 被调用的频率是每秒 60 次,也就是 1000/60 ,触发频率大概是 16.7ms 。(当执行复杂操作时,当它发现无法维持 60fps 的频率时,它会把频率降低到 30fps 来保持帧数的稳定。)

    简单而言,使用 requestAnimationFrame 来触发滚动事件,相当于上面的:

    throttle(func, xx, 1000/60) //xx 代表 xx ms内不会重复触发事件

    简单的示例如下:
    
    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);
  • 相关阅读:
    GridView只显示日期问题
    自定义一个选择日期的用户控件
    母版页所带来的路径问题
    C#之旅(一): 泛型 和IComparable、IComparer
    使用HttpWebRequest来秒杀
    NameValueCollection Dictionary区别
    在C#中使用代理的方式触发事件 (委托和事件 )(二)(转)
    SQL2005语句实现行转列,列转行
    值类型和引用类型的区别?(转)
    2010年年终总结
  • 原文地址:https://www.cnblogs.com/djdliu/p/5733078.html
Copyright © 2011-2022 走看看