requestAnimationFrame

動畫

web開發(fā)中實現(xiàn)動畫的方式有多種,CSS3中的transition和animation,js中的setInterval、setTimeout、canvas。H5新API requestAnimationFrame。

屏幕的刷新頻率

屏幕的刷新頻率即圖像在屏幕上每秒更新的次數(shù),單位為HZ。該值受屏幕分辨率、屏幕尺寸和顯卡的影響。每一次稱之為幀,一般的屏幕為60HZ,則一幀的時間為16.7ms左右。

setInterval動畫存在的問題

setInterval 其實就是通過設(shè)置一個間隔時間來不斷的改變圖像的位置,從而達到動畫效果的。但有時候會發(fā)現(xiàn),利用setInterval實現(xiàn)的動畫在某些低端機上會出現(xiàn)卡頓、抖動的現(xiàn)象。 這種現(xiàn)象的產(chǎn)生有兩個原因:

  1. setInterval 的執(zhí)行時間并不是確定的。js執(zhí)行時,setInterval 會被放進了異步隊列中,只有當主線程上的任務(wù)執(zhí)行完以后,才會去檢查該隊列里的任務(wù)是否需要開始執(zhí)行,因此 setInterval 的實際執(zhí)行時間一般要比其設(shè)定的時間晚一些。

  2. 屏幕的刷新頻率受分辨率和尺寸的影響,因此不同設(shè)備的屏幕刷新頻率可能會不同,而 setInterval 只能設(shè)置一個固定的時間間隔,這個時間不一定和屏幕的刷新時間相同,所以會引起丟幀從而出現(xiàn)卡頓現(xiàn)象。

requestAnimationFrame

window.requestAnimationFrame() 告訴瀏覽器——你希望執(zhí)行一個動畫,并且要求瀏覽器在下次重繪之前調(diào)用指定的回調(diào)函數(shù)更新動畫。該方法需要傳入一個回調(diào)函數(shù)作為參數(shù),該回調(diào)函數(shù)會在瀏覽器下一次重繪之前執(zhí)行。

優(yōu)勢:

requestAnimationFrame 比起 setTimeout、setInterval的優(yōu)勢主要有兩點:

  1. requestAnimationFrame 是由系統(tǒng)來決定回調(diào)函數(shù)的執(zhí)行時機。它會把每一幀中的所有DOM操作集中起來,在一次重繪或回流中就完成,并且重繪或回流的時間間隔緊緊跟隨屏幕的刷新頻率,不會引起丟幀和卡頓。
  2. 使用setTimeout實現(xiàn)的動畫,當頁面被隱藏或最小化時,setTimeout 仍然在后臺執(zhí)行動畫任務(wù),由于此時頁面處于不可見或不可用狀態(tài),刷新動畫是沒有意義的,完全是浪費CPU資源。而requestAnimationFrame則完全不同,當頁面處理未激活的狀態(tài)下,該頁面的屏幕刷新任務(wù)也會被系統(tǒng)暫停,因此跟著系統(tǒng)步伐走的requestAnimationFrame也會停止渲染,當頁面被激活時,動畫就從上次停留的地方繼續(xù)執(zhí)行,有效節(jié)省了CPU開銷。
用法:
window.requestAnimationFrame(callback)
回調(diào)參數(shù):

回調(diào)函數(shù)會被傳入DOMHighResTimeStamp參數(shù),DOMHighResTimeStamp指示當前被 requestAnimationFrame() 排序的回調(diào)函數(shù)被觸發(fā)的時間。

返回值:

返回整數(shù),即請求 ID ,是回調(diào)列表中唯一的標識。是個非零值,沒別的意義??梢詡鬟@個值給 window.cancelAnimationFrame() 以取消回調(diào)函數(shù)。

案例:

  1. 普通用法:
<div class="box"></div>
<button class="btn">click</button>
const box = document.querySelector('.box')
const btn = document.querySelector('.btn')

btn.addEventListener('click', () => {
  let start

  function handler(timestamp) {
    if (!start) start = timestamp

    const progress = timestamp - start
    start = timestamp

    console.log(progress)

    if (box.offsetWidth < 100) {
      box.style.width = `${box.offsetWidth + 1}px`
      box.innerHTML = box.offsetWidth + '%'

      requestAnimationFrame(handler)
    }
  }

  requestAnimationFrame(handler)
})
  1. 取消回調(diào)
<div class="main">
  <div class="content"></div>
  <button class="button">click</button>
</div>
const content = document.querySelector('.content')
const button = document.querySelector('.button')

button.addEventListener('click', () => {
  let rfaId
  let left = 0

  function handler() {
    if (content.offsetWidth < 100) {
      content.style.left = `${left++}px`

      if (left >= 100) {
        return cancelAnimationFrame(rfaId)
      }

      rfaId = requestAnimationFrame(handler)
    }
  }

  rfaId = requestAnimationFrame(handler)
})
  1. 自定義raf時間

自定義raf時間通常是放慢刷新頻率,畢竟系統(tǒng)的16.7ms已經(jīng)是正常的,再快也沒什么意義。

  <div class="main"></div>
  <button class="btn">click</button>
const element = document.querySelector('.main')
const btn = document.querySelector('.btn')

btn.addEventListener('click', () => {
  // 自定義的動畫時間差
  const time = 50
  let left = 0
  let flag = true
  // 當前執(zhí)行時間
  let nowTime = Date.now()
  // 每次動畫執(zhí)行結(jié)束的時間
  let lastTime = Date.now()

  // 執(zhí)行動畫
  function handler() {
    nowTime = Date.now()

    if (nowTime - lastTime >= time) {
      lastTime = nowTime

      if (flag) {
        if (left <= 100) {
          element.style.left = `${left++}px`
        } else {
          flag = false
        }
      } else {
        if (left >= 0) {
          element.style.left = `${left--}px`
        } else {
          flag = true
        }
      }
    }

    requestAnimationFrame(handler)
  }
  requestAnimationFrame(handler)
})

當然,該API的用途不止設(shè)置動畫,還可以用于節(jié)流、防抖等。比如lodash關(guān)于節(jié)流、防抖的就是基于此API實現(xiàn)的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容