requestAnimationFrame
requestAnimationFrame
采用系统时间间隔,保持最佳绘制效率,一般浏览器重绘频率为1000ms/60帧是16.7ms;所以一旦小于这个值,浏览器就会重复绘制消耗性能;
const timer = time=> console.log('animation called at', time) // 这样就会在同一帧内绘制了两次动画 requestAnimationFrame(timer) requestAnimationFrame(timer)
所以在一些高频绘制的场景比如scroll等,就会造成过度绘制的问题;
window.addEventListener('scroll', e => { requestAnimationFrame(time => { timer(time) }) })
解决方法:通过requestAnimationFrame来管理队列,其思路就是保证requestAnimationFrame的队列里,同样的回调函数只有一个;
let s = () => { let bool; if(bool) {return} bool = true; requestAnimationFrame(time => { bool = false; timer(time); }) } window.addEventListener('scroll', s)
特点:
- requestAnimationFrame会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率;
- 在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的CPU、GPU和内存使用量;
- requestAnimationFrame是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销;
- requestAnimationFrame返回一个整数,表示定时器的编号,这个值可以传递给cancelAnimationFrame用于取消这个函数的执行;
- cancelAnimationFrame可以取消绘制;
<html> <style> #myDiv{ background: #abcdef;width: 0;height:2rem;line-height:2rem; } </style> <body> <div id='myDiv'>0%</div> <button id='btn'>btn</button> <script> var timer; btn.onclick=()=>{ myDiv.style.width='0'; cancelAnimationFrame(timer); timer = requestAnimationFrame(function fn(){ if(parseInt(myDiv.style.width)<500){ myDiv.style.width = parseInt(myDiv.style.width)+5+'px'; myDiv.innerHTML = parseInt(myDiv.style.width)/5+'%'; timer = requestAnimationFrame(fn); }else{ cancelAnimationFrame(timer) } }) } </script>
</body>
</html>
requestIdleCallback
- requestIdleCallback回调的执行的前提条件是当前浏览器处于空闲状态;
- requestAnimationFrame的回调会在每一帧确定执行,属于高优先级任务,而requestIdleCallback的回调则不一定,属于低优先级任务;如果1帧内有空闲时间,就可以执行;如果浏览器一直繁忙,可以传入第二个参数{timeout: 2000}
- requestIdleCallback回调中不推荐dom操作;