防抖函数,减少事件在一定时间内的触法次数:
<script> window.onload = function(){ function debounce(func,wait,immediate){//一个简单的防抖函数 var timer = null;//定时器 return function(){ clearTimeout(timer);//每次触发scroll handler是先将定时器清除 timer = setTimeout(func,wait);//指定xx毫秒后真正想执行的操作 } } function realFunction(){//实际想绑定在scroll事件上的handler console.log('hi'); } //采用防抖 window.addEventListener('scroll',debounce(realFunction,500)); } </script>
封装一下:
<script> window.onload = function(){ function debounce(func,wait,immediate){ var timer; return function(){ var context = this,args = arguments; var later = function(){ timer = null; if(!immediate){ func.apply(context,args); } }; var callNow = immediate && !timer; clearTimeout(timer); timer = setTimeout(later,wait); if(callNow){ func.apply(context,args); } } }; var myEfficientFn = debounce(function(){ console.log('hi'); },500); window.addEventListener('scroll',myEfficientFn); } </script>
但是防抖函数也存在一些问题,带来一些用户体验不太好的地方,比如图片可能会在用户停止scroll的时候才被加载出来.我们希望即使页面在不断被滚动,但是滚动handler也可以以一定频率被触发,这种技巧被称为节流函数:
只允许一个函数在X毫秒内执行一次,只有当上一次函数执行后过了规定的时间间隔才能进行下一次该函数的调用.在规定时间内至少执行一次我们希望触发的事件handler
如果在一段时间内scroll触发的间隔一直短于100ms,那么能保证事件我们希望调用的handler至少在500ms内会被触发一次
<script> window.onload = function(){ //简单的节流函数 function throttle(func,wait,mustRun){ var timer; var startTime = new Date(); return function(){ var context = this; var args = arguments; var curTime = new Date(); clearTimeout(timer); //如果到达了规定的触发时间间隔,触发handler if(curTime - startTime >= mustRun){ func.apply(context,args); startTime = curTime; //没有到达就重新设定定时器 }else{ timer = setTimeout(func,wait); } }; }; function realFunction(){ console.log('hi'); } window.addEventListener('scroll',throttle(realFunction,500,1000)) } </script>
3.使用rAF(requestAnimationFrame)触发滚动事件
如果只需要兼容高版本的浏览器,又或者页面追求高精度的结果,那么可以使用浏览器的原生方法rAF(requestAnimationFrame);
requestAnimationFrame:
window.requestAnimationFrame()这个方法是用来在页面重绘之前,通知浏览器调用一个指定的函数,这个方法接受一个函数为参数,该函数会在重绘前调用,
requestAnimationFrame通常用于web动画的制作,用于准确控制页面的帧刷新渲染,让动画效果更加流畅,同时它也 是一个定时器,被调用的频率是每秒60次,也就是16.7Ms
简单地说,使用requestAnimationFrame来触发滚动事件大概类似于:
throttle(func,xx,16.7)//xx代表在xx ms内不会重复触发事件handler大概功能就是在滚动的过程中,保持以16.7ms的频率触发事件
<script> window.onload = function(){ var ticking = false;//requestAnimationFrame触发锁 function onScroll(){ if(!ticking){ requestAnimationFrame(realFunc); ticking = true; } } function realFunc(){ console.log('hi'); ticking = false; } window.addEventListener('scroll',onScroll,false); } </script>
requestAnimationFrame比节流函数更加适用于复杂的场景,但是存在兼容性方面的问题,而且可控性也不好.
4.简化事件内操作
我们应该简化事件内操作,将一些变量的初始化,不依赖于位置变化的计算等都当在scroll外提前就绪.
1.避免在scroll事件中修改样式属性/将样式属性从scroll事件中剥离
输入事件处理函数,比如scroll/touch事件的处理,都会在requestAnimationFrame之前被调用执行,因此,如果你在scroll事件处理函数中做了样式修改属性的操作,那么这些操作会被浏览器暂存起来,然后调用requestAnimationFrame的时候,如果你在一开始做了读取样式属性的操作,那么这将会触发浏览器的强制同步.
2.滑动过程中尝试使用pointer-events:none 禁止鼠标事件可以提高滚动时的帧频,但是使用该属性后hover click事件会全部失效,要在停止滚动的时候移除掉这个属性