首先 了解下 setTimeout的浅层原理,setTimeout 是通过浏览器异步API执行,执行完成之后,回调交给宏任务,假设宏任务队列已经有其他任务,就会导致 setTimeout 回调执行延后,从而不精准。
浏览器1s执行60帧,大概两帧之间的执行时间差为16ms,认为这样是比较好的体验。
requestAnimationFrame 是不需要设定时间,大概16ms执行一次。所以使用requestAnimationFrame 会更准确
<!--使用requestAnimationFrame 实现setInterval -->
export function mySetInterval(cb, cancelCb) {
let timer = null;
let pre = new Date()
let fn = function() {
timer = requestAnimationFrame(() => {
let cur = new Date()
if (cur - pre >= 1000) {
cb()
pre = cur
}
timer = requestAnimationFrame(fn)
if (cancelCb && cancelCb()) {
timer && cancelAnimationFrame(timer)
}
})
}
fn()
}
<!-- 倒计时 -->
export default class Countdown {
constructor({ endTime }) {
this.endTimetamp = new Date(endTime).getTime()
console.log(endTime, this.endTimetamp);
this.countDownDate = {}
mySetInterval(this.countDown.bind(this), this.cancel.bind(this))
}
countDown() {
let dis = (this.endTimetamp - new Date().getTime()) / 1000
this.countDownDate = this.calculator(dis)
console.log('发送事件---->', this.countDownDate);
// this.on('countdown', this.countDownDate)
}
cancel() {
return this.countDownDate && this.countDownDate.timetamp <= 0 ? true : false
}
calculator(second) {
let sec = 1,
min = 60 * sec,
hour = 60 * min,
day = 24 * hour;
return {
day: parseInt(second / day),
hour: parseInt(second % day / hour),
min: parseInt(second % hour / min),
second: parseInt(second % min),
timetamp: second
}
}
}
export default Countdown
new Countdown({
endTime: '2021-11-30 12:00'
})
优点:
- 执行准时
- 与组件逻辑解藕