浏览器高性能滑动解决方案

vinsonli 8年前
   <p>最近一段时间,在项目中实现过瀑布流、懒加载、侧边栏导航等功能。总觉得在处理浏览器scroll滑动的时候有点问题,通过计算各个模块的高度和某些指定模块的出现时机时,在PC端还好,在移动端容易出现卡顿和抖动的情况。特此整理解决方案。</p>    <h2>在停止滑动后执行</h2>    <p>如果我们需要在滑动的时候进行某些操作,可以在停止滑动后再延迟进行,这样就不会一边滑动一边执行了。</p>    <pre>  <code class="language-javascript">//滑动停止后延迟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);  </code></pre>    <p>还可以更好的封装一番:</p>    <pre>  <code class="language-javascript">// 防抖动函数  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);  </code></pre>    <h2>节流,在滑动中间隔执行</h2>    <p>如果我们不是在滑动停止后执行,而是在滑动中需要实时计算一些东西,就可以采用 <strong>节流</strong> 的方式。</p>    <p>节流函数,只允许一个函数在 X 毫秒内执行一次。</p>    <pre>  <code class="language-javascript">// 简单的节流函数  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));  </code></pre>    <p>如果在一段时间内 scroll 触发的间隔一直短于 500ms ,那么能保证事件我们希望调用的 handler 至少在 1000ms 内会触发一次。</p>    <h2>高精度控制</h2>    <p>上面的两种方式都是通过setTimeout来实现的,精度不够高,如果对浏览器兼容性要求不高,或者是移动端web,可以使用原生的 requestAnimationFrame 来实现。</p>    <p>该方法的原理: 在浏览器的页面重绘之前,通知浏览器调用一个指定的函数。该方法被调用的频率为每秒60次 。</p>    <p>所以说用该方法来触发滚动事件,相当于上面的:</p>    <pre>  <code class="language-javascript">throttle(func, xx, 1000/60) //xx 代表 xx ms内不会重复触发事件 handler  </code></pre>    <pre>  <code class="language-javascript">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);  </code></pre>    <p>这样浏览器就会以16.7ms的频率触发事件。</p>    <p>至于移动端,最好还是使用iscroll这样的模拟事件滑动的库来解决其延迟问题吧。</p>    <p> </p>    <p>来自: <a href="/misc/goto?guid=4959674300079358219" rel="nofollow">http://brizer.github.io/2016/06/11/浏览器高性能滑动解决方案/</a></p>    <p> </p>