# requestAnimationFrame
# 早期的动画
- 早期时刻,定时器以及定时执行都是 js 动画执行的重要工具
 
;(function () {
  function updateAnimations() {
    doAnimation1()
    doAnimation2()
    // 其他任务
  }
  setInterval(updateAnimations, 100)
})()
 1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
- 一般的计算器显示器的屏幕刷新频率是 60HZ,这就意味着是每秒要绘制 60 次。大多数浏览器都会显示绘制频率。
 - 为了实现动画效果过渡平滑最佳的重绘间隔大约 17/毫秒。以这个速度重绘可以实现最佳的滑动效果。
 - 但是即使是这样依然无法保证能平滑过度。因为延时器/定时器的执行的时机不定。虽然可以设置执行时间。即使将延时任务添加到队列中也无法保证能立马执行。这取决于上一个任务或是同步任务执行的时间长度
 - 从 js 层面无法得知浏览器的绘制时机。所以函数
 requestAnimationFrame就出现了
# 函数requestAnimationFrame
 # 1. 执行时机
- 在每次浏览器重绘之前执行(
修改的内容会随着新的绘制,再次显示到页面上) 
# 2. 使用方法
- 函数
requestAnimationFrame只允许传递一个参数。而且参数必须是一个函数 - 参数函数会被加入到队列。在每次浏览器绘制之前将队列中的函数进行执行
 - 每次将执行函数添加到队列后,只能保证当前执行,下次执行需要再次添加
 
function updateProgress() {
  var div = document.getElementById('status')
  div.style.width = parseInt(div.style.width, 10) + 5 + '%'
  if (div.style.left != '100%') {
    requestAnimationFrame(updateProgress)
  }
}
requestAnimationFrame(updateProgress)
 1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 3. 如何取消
在全局中提供了函数
cancelAnimationFrame. 可以通过函数进行取消。大致的原理跟延时器/ 定时器保持一致
- 在函数
requestAnimationFrame将函数加入队列后,会返回一个 id(可以将id理解为队列的位置)。可以通过执行函数cancelAnimationFrame来进行取消 
let requestID = window.requestAnimationFrame(() => {
  console.log('Repaint!')
})
window.cancelAnimationFrame(requestID)
 1
2
3
4
2
3
4
# 4. 不知道自己的代码何时执行怎么办
- 传给 requestAnimationFrame()的函数实际上可以接收一个参数
 此参数是一个 DOMHighResTimeStamp 的实例(比如 performance.now()返回的值),表示下次重绘的时间。这一点非常重要:- requestAnimationFrame()实际上把重绘任务安排在了未来一个已知的时间点上,而且通过这个参数告诉了开发者。基于这个参数,就可以
 更好地决定如何调优动画了
# 5. 通过函数requestAnimationFrame进行节流处理
 let enabled = true
function expensiveOperation() {
  console.log('Invoked at', Date.now())
}
window.addEventListener('scroll', () => {
  if (enabled) {
    enabled = false
    window.requestAnimationFrame(expensiveOperation)
    window.setTimeout(() => (enabled = true), 50)
  }
})
 1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11