window.requestAnimationFrame定时器与浏览器重绘
Posted by Mars . Modified at
window.requestAnimationFrame API的功能:精确控制函数在重绘前时间节点执行。
1. 有关浏览器重绘机制的几个事实
- 大多数浏览器会限制重绘频率,一般不大于屏幕刷新率60Hz;
- 浏览器发生重绘的时机不确定,但是两次重绘时间间隔一定不小于(1/60)s,也就是大约16ms;
- requestAnimationFrame API传入一个函数,控制这个函数精确在紧邻浏览器下次重绘前被调用。
2. requestAnimationFrame的功能和机制
requestAnimationFrame
是浏览器提供的一个按帧对网页进行重绘的 API,是为了创建动画而设计的;- 传统的
setTimeout
和setInterval
定时器,确定的只是回调函数加入到任务队列中的时间,而无法确定函数实际执行的时间(前面可能还有别的任务未执行完); requestAnimationFrame
采用系统时间来确定间隔,可以保证传入的函数fn
,精确地在下一次屏幕刷新前时间点执行;requestAnimationFrame
在页面非可见状态下,不会执行传入的函数,而是将他们保存在一个执行队列中,然后在页面恢复可见后立即依次执行;- requestAnimationFrame(fn1)中,如果fn1内部再次调用requestAnimationFrame(fn2),则传入的函数fn2会被放入下下次重绘前执行。
3. 取消requestAnimationFrame
和setTimeout一样,通过返回的id进行取消。
let a = requestAnimationFrame()
// 手动取消
cancelAnimationFrame(a)
取消后,requestAnimationFrame(fn)传入函数fn被从重绘前执行的回调队列中清除。
4. 典型应用:过渡的计数器
效果:
// Marswiz @2021
let cur = 0;
let counting = false;
function count(target = 600, step = 10, anchor = '#counter') {
counting = true;
let container = document.querySelector(anchor);
let i = cur;
const show = function() {
// 调用 requestAnimationFrame
requestAnimationFrame(() => {
container.innerText = i;
i += step;
cur += step;
// 在 requestAnimationFrame 内部实现递归调用
if (i <= target) show();
else {
// 递归出口
container.innerText = target;
cur = target;
counting = false;
}
});
}
show();
}
document.querySelector('#btn-100').addEventListener('click', () => {
if (!counting) count(cur + 100, Math.floor(100/33));
});
document.querySelector('#btn-500').addEventListener('click', () => {
if (!counting) count(cur + 500, Math.floor(500/33));
});