当前位置: 首页 > Web前端 > HTML

功能除颤-节流以提高性能+本机滚动以在滚动到视口时显示

时间:2023-04-02 13:18:39 HTML

前端开发中的一个老生常谈的问题是'当用户滚动时,根据滚动位置适当触发不同的功能/动画,例如当元素出现在视口中时,触发元素的样式变化。通常的做法是附加scrollElementscoll事件。但是我们知道,滚动条滚动时,scroll事件的触发非常频繁,而且不受JS控制(浏览器的事件队列是原生提供的),如图(添加事件监听后,滚轮有三格):根据系统设置,一次滚轮触发的滚动事件约为10~15次。如果在回调函数中加入大量的DOM操作或计算,会造成明显的卡顿等性能问题。有没有办法淡化回调函数?触发操作呢?这时候就需要函数节流(throttle)和去抖(debounce)来解决!2017-02-06更新功能版本该版本使用闭包封装数据,并修正这一点以增强健壮性,去除开头显示在视口上的元素说便宜,这里是代码//根据单个元素,throttle函数负责事件稀释,接受两个参数:间隔调用的函数和调用间隔。varthrottle=function(fn,interval){letstart=Date.now()letfirst=truereturnfunction(...args){letnow=Date.now()//如果这是第一次调用,忽略intervalif(first){fn.apply(this,args)first=falsereturn}if(now-start>interval){fn.apply(this,args)start=now}}}//IIFEvarshowElems=(函数(selector,func){//预处理,识别已经显示在视口中的元素letelemCollect=[...document.querySelectorAll(selector)]letinnerHeight=window.innerHeightlethiddenElems=[]elemCollect.forEach((elem,index)=>{lettop=elem.getBoundingClientRect().top//只加入判断队列if(top>innerHeight){hiddenElems.push(elem)}})//释放内存elemCollect=nullreturnfunction(...args){hiddenElems.forEach((elem)=>{letbottom=elem.getBoundingClientRect().bottomif(bottom某个值,比如500ms,则执行线路回调,同时设置ifOperationBegin=true开始下一个设置开始时间->记录操作时间->判断的循环。具体代码实现:varscrollBegin=false,scrollStartTime=null;//用户还没有开始操作document.addEventListener('scroll',function(){if(!scrollBegin)scrollStartTime=Date.now();//记录开始时间,前提是没有触发回调scrollBegin=true;//Setflagif(Date.now()-scrollStartTime>500){//如果操作时间和开始时间的间隔大于500ms,则exec(elems,cb);//回调scrollBegin=false;//flag设置为false设置新的开始时间}})这样做的效果是当用户继续触发滚动操作时,保证在用户操作过程中至少每500ms触发一次回调.如果用户操作在500ms以内结束,则无所谓。Next当用户重新开始操作时,我们的scrollStartTime仍然保留,会立即触发回调。这两项技术在实践中可以应用到哪些地方呢?请看下面的代码示例:functiondetectVisible(selector,cb,interval){//检测元素是否在视口中的函数varelems=document.querySelectorAll(selector),innerHeight=window.innerHeight;varexec=function(elems,cb){//回调函数Array.prototype.forEach.call(elems,function(elem,index){if(elem.getBoundingClientRect().topinterval){exec(elems,cb);console.log('由油门调用!')detectVisible.scrollBegin=false;}detectVisible.timer=setTimeout(function(){exec(elems,cb);console.log('由debounce调用!')},interval)})}detectVisible('div.elem',function(elem){this.style.backgroundColor='yellow';},500);本例中,我们结合throttle和debounce实现了如下效果:当滚动停止时,至少每500ms触发一次回调;判断函数也会在用户停止滚动后500ms触发一次。可以打开控制台查看回调是什么时候throttle触发的,什么时候debounce触发的。总结这篇文章文章的主题是稀释事件以提高性能。有两种解决方案:节流和去抖动。前者是在用户操作时通过判断操作时间来达到定时触发回调的效果;而后者则是触发时间不断延长,直到用户停止操作才执行回调。两者各有优缺点。将两者结合起来,我们就得到了无论用户如何操作(不间断操作或者操作时间很短)都可以保证定时执行的回调函数。问题解决了!看完这篇文章,如果你有收获,请到github上给我一个star吧!~