手写一个通用的 animate 动画工具函数:支持多属性插值、动画时长、缓动函数、每帧回调,可被手动中断,并返回 Promise(参考 PDD 前端笔试题)。

实现

function animate({ duration, easing = t => t, onUpdate, onAbort }) {
  let startTime = null
  let aborted = false
  let rafId = null

  const promise = new Promise((resolve) => {
    function step(now) {
      if (aborted) {
        onAbort && onAbort()
        resolve({ done: false, aborted: true })
        return
      }
      if (startTime === null) startTime = now
      const elapsed = now - startTime
      const progress = Math.min(elapsed / duration, 1) // 0~1
      const eased = easing(progress)                    // 缓动后进度
      onUpdate && onUpdate(eased, progress)

      if (progress < 1) {
        rafId = requestAnimationFrame(step)
      } else {
        resolve({ done: true, aborted: false })
      }
    }
    rafId = requestAnimationFrame(step)
  })

  return {
    finished: promise,
    abort() { aborted = true; cancelAnimationFrame(rafId) },
  }
}

使用示例

const anim = animate({
  duration: 1000,
  easing: t => 1 - Math.pow(1 - t, 3), // easeOutCubic
  onUpdate: (eased) => {
    box.style.opacity = eased
    box.style.transform = 'translateX(' + (eased * 200) + 'px)'
  },
})

setTimeout(() => anim.abort(), 500) // 中途中断
await anim.finished
console.log('动画结束或被中断')

考察点

同分类其他题目