封装 requestAnimationFrame 来代替 setTimeout

·  阅读 7336

最近看antdv 某些组件源码的时候发现,是使用requestAnimationFrame 来代替 setTimeout;下面我们就来看看是如何实现的;

首先 requestAnimationFrame与setTimeout相比,requestAnimationFrame 最大的优势是由浏览器来决定回调函数的执行时机,即紧跟浏览器的刷新步调。

其次,CPU节能:使用setTimeout实现的动画,当页面被隐藏(隐藏的)或最小化(后台标签页)时,setTimeout仍然在后台执行动画任务(只不过执行时间有所延迟),由于此时页面处于不可见或不可用状态,刷新动画是没有意义的,而且还浪费 CPU 资源和电池寿命。而requestAnimationFrame则完全不同,当页面处于未激活的状态下,该页面的屏幕绘制任务也会被浏览器暂停,因此跟着浏览器步伐走的requestAnimationFrame也会停止渲染,当页面被激活时,动画就从上次停留的地方继续执行,有效节省了 CPU 开销,提升性能和电池寿命。

// getRequestAnimationFrame.js
// 判断是否支持 requestAnimationFrame/cancelAnimationFrame |setTimeout/clearTimeout,根据支持语法返回对应的方法

const availablePrefixs = ['moz', 'ms', 'webkit']

function requestAnimationFramePolyfill () {
  let lastTime = 0
  return function (callback) {
    const currTime = new Date().getTime()
    const timeToCall = Math.max(0, 16 - (currTime - lastTime))
    const id = window.setTimeout(function () {
      callback(currTime + timeToCall)
    }, timeToCall)
    lastTime = currTime + timeToCall
    return id
  }
}

export default function getRequestAnimationFrame () {
  if (typeof window === 'undefined') {
    return () => {}
  }
  if (window.requestAnimationFrame) {
    // https://github.com/vuejs/vue/issues/4465
    return window.requestAnimationFrame.bind(window)
  }

  const prefix = availablePrefixs.filter(key => `${key}RequestAnimationFrame` in window)[0]

  return prefix ? window[`${prefix}RequestAnimationFrame`] : requestAnimationFramePolyfill()
}

export function cancelRequestAnimationFrame (id) {
  if (typeof window === 'undefined') {
    return null
  }
  if (window.cancelAnimationFrame) {
    return window.cancelAnimationFrame(id)
  }
  const prefix = availablePrefixs.filter(
    key => `${key}CancelAnimationFrame` in window || `${key}CancelRequestAnimationFrame` in window
  )[0]

  return prefix
    ? (
      window[`${prefix}CancelAnimationFrame`] || window[`${prefix}CancelRequestAnimationFrame`]
    ).call(this, id)
    : clearTimeout(id)
}

复制代码
// requestAnimationTimeout.js
// 创建自定 方法 requestAnimationTimeout 用于延迟执行
// 创建自定 方法 cancelAnimationTimeout 用于清除还未执行的requestAnimationTimeout
import getRequestAnimationFrame, {
  cancelRequestAnimationFrame
} from './getRequestAnimationFrame'

const dataSetTimeout = getRequestAnimationFrame()

export const cancelAnimationTimeout = frame => cancelRequestAnimationFrame(frame.id)

export const requestAnimationTimeout = (callback, delay = 0) => {
  const start = Date.now()
  function timeout () {
    if (Date.now() - start >= delay) {
      callback.call()
    } else {
      obj.id = dataSetTimeout(timeout)
    }
  }

  const obj = {
    id: dataSetTimeout(timeout)
  }

  return obj
}
复制代码

使用就直接引入 requestAnimationTimeout.js 导出其 {requestAnimationTimeout, cancelAnimationTimeout } 方法;

requestAnimationTimeout 方法会返回一个 object 对象, 包含一个id值;如果需要取消执行,cancelAnimationTimeout(/* 将 requestAnimationTimeout 方法会返回一个 object 对象,作为参数*/);

希望能对你有帮助!

分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改