用requestAnimationFrame替换原生setInterval

222 阅读1分钟

记个笔记!

requestAnimationFrame是什么? 查看MDN

为什么这么做?有以下好处:

  1. 计时更准确,原生setInterval在执行过程中会产生时间偏差。(当然,也可以二次封装setInterval去除这个误差)
  2. 避免内存泄漏,在浏览器处于最小化状态或者切换到其他tab时(即当前网页处于不可见状态)原生setInterval依然会继续执行,造成内存泄漏;而requestAnimationFrame则没有这个问题,因为它是在浏览器下一次重绘之前执行传递的回调函数,当前网页不可见的时候,是不会进行重绘操作的,所以此时回调函数不会被执行。
  3. 不会丢帧,且性能更高,采用系统时间间隔,保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,增加开销;也不会因为间隔时间太长,使动画卡顿不流畅
import { ref } from 'vue'
/**
 * 用于替换原生setInterval
 * @param fn 函数
 * @param interval ms 执行间隔
 * @return 函数 取消定时器的函数
 * */
export function useInterval(fn: () => void, interval = 1000) {
  let nowTime = ref(performance.now())
  let preTime: number
  let timeDifferance = 0 //每次的时间偏差
  let timer: number
  function refresh() {
    preTime ??= performance.now()
    if (preTime) {
      nowTime.value = performance.now()
      const realInterval = interval - timeDifferance
      if (nowTime.value - preTime >= realInterval) {
        timeDifferance = (nowTime.value - preTime - realInterval) % realInterval
        preTime = performance.now()
        fn()
        console.log('时间误差:' + timeDifferance + 'ms')
      }
    }
    timer = requestAnimationFrame(refresh)
  }
  timer = requestAnimationFrame(refresh)
  // 立即执行
  fn()
  function cancelTimer() {
    cancelAnimationFrame(timer)
  }
  // 离开后销毁
  onUnmounted(() => {
    cancelAnimationFrame(timer)
  })
  
  return cancelTimer
}

如果有什么疑问或者建议,可以多多交流,文笔有限,才疏学浅,文中若有不正之处,万望告知。

参考文档:

  1. developer.mozilla.org/zh-CN/docs/…
  2. www.apispace.com/news/post/5…