raf 定时器
大约 3 分钟
raf 定时器
requestAnimationFrame
requestAnimationFrame()
是用于在浏览器下一次 重绘 之前请求执行一个动画帧的方法。
requestAnimationFrame()
接收一个回调函数作为参数,回调函数会在浏览器下一次重绘之前被调用。它返回一个整数,代表 requestAnimationFrame()
在任务队列中的 ID 标识,可以使用这个 ID 作为参数调用 window.cancelAnimationFrame()
方法来取消回调函数的执行。回调函数中会被传入一个 timestamp
(时间戳) 参数,表示当前开始执行回调的时间。
requestAnimationFrame()
是一次性的,就是说它只会在下一帧中执行传入的回调,不会重复执行,如果想要连续的执行回调,就需要在每一帧中使用 requestAnimationFrame()
方法。
function animate() {
// 动画代码
console.log('动画帧');
// 在下一次重绘之前再次调用 animate
requestAnimationFrame(animate);
}
// 启动动画
requestAnimationFrame(animate);
ℹ️提示
requestAnimationFrame()
的执行次数通常是每秒 60 次,即大约 16.6ms 执行一次。这个执行次数通常与浏览器屏幕刷新次数相匹配。它相比于 setTimeout()
定时器的优点是:
- 更高的帧率和更流畅的动画:
requestAnimationFrame()
会在每一帧动画开始时被调用,而且它会自动调整以匹配浏览器的刷新率。这意味着动画会以更高的帧率(通常是 60 帧每秒)运行,从而创建更流畅的动画。 - 更好的性能:
requestAnimationFrame()
只在浏览器准备好绘制新的帧时才会运行,这可以避免不必要的计算和重绘,从而提高性能。 - 自动的时间间隔:
requestAnimationFrame()
会自动计算出下一帧的最佳时间间隔,你不需要手动设置时间间隔,这可以避免因为间隔设置不当导致的动画卡顿。
定时器
在简单了解了 requestAnimationFrame()
的基本用法以及特点后,我们可以利用它的特点来模拟实现 setTimeout()
与 setInterval()
的功能。
以下是具体实现代码:
class Timer {
constructor() {
//记录定时器数据
this.record = {}
this.minInterval = 1000 / 60
}
loop(type, callback, delay) {
if (!['interval', 'timeout'].includes(type)) return;
if (!this.record[type]) this.record[type] = {}
//记录开始时间
let startTime = Date.now();
const record = Symbol(type);
//循环执行计算时间间隔
const loopStart = () => {
this.setRecord(record, type, loopStart);
//获取当前时间
let currentTime = Date.now();
if (currentTime - startTime >= delay) {
if (type == "timeout") {
//此条件执行timeout
callback && callback(currentTime);
this.clear({ record, type })
} else if (type == "interval") {
//此条件执行interval
startTime = Date.now();
callback && callback(currentTime);
}
}
}
//定时! 启动!
this.setRecord(record, type, loopStart);
return { record, type }
}
//记录 raf 的id
setRecord(record, type, callback) {
const id = window.requestAnimationFrame(callback)
this.record[type][record] = id;
}
//模拟 setInterval 方法
interval(callback, delay = 1000) {
delay = delay < this.minInterval ? this.minInterval : delay;
return this.loop("interval", callback, delay);
}
//模拟 setTimeout 方法
timeout(callback, delay = 1000) {
delay = delay < this.minInterval ? this.minInterval : delay;
return this.loop("timeout", callback, delay);
}
//清除定时器方法
clear(timer) {
if(!timer) return
const { record, type } = timer
window.cancelAnimationFrame(this.record[type][record]);
this.record[type][record] = null
}
}
//使用
const timer = new Timer()
let count = 0
let id1 = timer.interval((stamp)=>{
console.log(stamp)
count++
if(count > 5){
timer.clear(id1)
console.log('clear interval')
}
},1000)
let id2 = timer.timeout((stamp)=>{
console.log('hello')
timer.clear(id2)
console.log('clear timeout')
},3000)
ℹ️提示
更多 requestAnimationFrame()
的相关信息请看 window.requestAnimationFrame()