成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

前端動(dòng)畫(huà)技術(shù)的研究和比較

endiat / 2766人閱讀

摘要:它和前端動(dòng)畫(huà)之間沒(méi)有包含與被包含的關(guān)系,更不能將它們混為一談,只有兩者的有機(jī)結(jié)合才能創(chuàng)建出炫酷的界面。

第一次在segmentfault上發(fā)文章 :),歡迎評(píng)論指正。原文最初發(fā)表在 https://github.com/WarpPrism/...

動(dòng)畫(huà)相關(guān)概念

幀:動(dòng)畫(huà)過(guò)程中每一個(gè)靜止的狀態(tài),每一張靜止的圖片

幀率:刷新頻率,每秒鐘播放的幀數(shù),F(xiàn)PS(frame per second),單位是Hz

幀時(shí)長(zhǎng):每一幀停留的時(shí)間,如60FPS的動(dòng)畫(huà)幀時(shí)長(zhǎng)約為16.7ms,意味著瀏覽器必須在16.7ms內(nèi)繪制完這一幀

硬件加速:硬件有三個(gè)處理器:CPU、GPU和APU(聲音處理器)。他們通過(guò)PCI/AGP/PCIE總線交換數(shù)據(jù)。GPU在浮點(diǎn)運(yùn)算、并行計(jì)算等部分計(jì)算方面,明顯高于CPU的性能。硬件加速即利用GPU進(jìn)行動(dòng)畫(huà)的計(jì)算

緩動(dòng):最普通的動(dòng)畫(huà)就是勻速的動(dòng)畫(huà),每次增加固定的值。緩動(dòng)就是用來(lái)修改每次增加的值,讓其按照不規(guī)律的方式增加,實(shí)現(xiàn)動(dòng)畫(huà)的變化。

瀏覽器的刷新率:通常為60Hz

前端動(dòng)畫(huà)分類

從控制角度分,前端動(dòng)畫(huà)分為兩種:

JavaScript控制的動(dòng)畫(huà)

CSS控制的動(dòng)畫(huà)

JS動(dòng)畫(huà)

JS動(dòng)畫(huà)的原理是通過(guò)setTimeout setIntervalrequestAnimationFrame 方法繪制動(dòng)畫(huà)幀(render),從而動(dòng)態(tài)地改變網(wǎng)頁(yè)中圖形的顯示屬性(如DOM樣式,canvas位圖數(shù)據(jù),SVG對(duì)象屬性等),進(jìn)而達(dá)到動(dòng)畫(huà)的目的。

多數(shù)情況下,應(yīng) 首先選用 requestAnimationFrame方法(RAF),因?yàn)镽AF的原理是會(huì)在瀏覽器下一次重繪之前更新動(dòng)畫(huà),即它的刷新頻率和瀏覽器自身的刷新頻率保持一致(一般為60Hz),從而確保了性能。另外RAF在瀏覽器切入后臺(tái)時(shí)會(huì)暫停執(zhí)行,也可以提升性能和電池壽命。(來(lái)自MDN)

// requestAnimationFrame Demo
let i = 0
let render = () {
  if (i >= frame.length) i = 0
  let currentFrame = frame[i]
  drawFrame(currentFrame)
  i++
  requestAnimationFrame(render)
}
requestAnimationFrame(render)

下面代碼是一個(gè)用js + canvas 實(shí)現(xiàn)幀動(dòng)畫(huà)的一個(gè)例子,可以幫你更好的理解js動(dòng)畫(huà)原理:

/**
 * 基于canvas的幀動(dòng)畫(huà)庫(kù)
 * 最近修改日期:2018-06-22
 */

import { IsArray } from "Utils"

class FrameAnim {
  constructor ({ frames, canvas, fps, useRAF }) {
    this._init({ frames, canvas, fps, useRAF })
  }
  /**
   * 實(shí)例初始化
   * @param options ->
   * @param {Array} frames image對(duì)象數(shù)組
   * @param {Object} canvas canvas dom 對(duì)象
   * @param {Number} fps 幀率
   * @param {Boolean} useRAF 是否使用requestAnimationFrame方法
   */
  _init ({ frames, canvas, fps, useRAF }) {
    this.frames = []
    if (IsArray(frames)) {
      this.frames = frames
    }
    this.canvas = canvas
    this.fps = fps || 60
    this.useRAF = useRAF || false

    this.ctx = this.canvas.getContext("2d") // 繪圖上下文
    this.cwidth = this.canvas.width
    this.cheight = this.canvas.height
    this.animTimer = null // 動(dòng)畫(huà)定時(shí)器
    this.currentIndex = 0 // 當(dāng)前幀
    this.stopLoop = false // 停止循環(huán)播放
  }
  _play (frameSections, fromIndex = 0) {
    return new Promise((resolve, reject) => {
      this.currentIndex = fromIndex || 0
      if (this.useRAF) {
        let render = () => {
          this.ctx.clearRect(0, 0, this.cwidth, this.cheight)
          let currentFrame = frameSections[this.currentIndex]
          this.ctx.drawImage(currentFrame, 0, 0, currentFrame.width, currentFrame.height)
          this.currentIndex++
          if (this.currentIndex <= frameSections.length - 1) {
            requestAnimationFrame(render)
          } else {
            this._stopPlay()
            resolve({finish: true})
          }
        }
        this.animTimer = requestAnimationFrame(render)
      } else {
        this.animTimer = setInterval(() => {
          if (this.currentIndex > frameSections.length - 1) {
            this._stopPlay()
            resolve({finish: true})
            return
          }
          this.ctx.clearRect(0, 0, this.cwidth, this.cheight)
          let currentFrame = frameSections[this.currentIndex]
          this.ctx.drawImage(currentFrame, 0, 0, currentFrame.width, currentFrame.height)
          this.currentIndex++
        }, 1000 / this.fps)
      }
    })
  }
  _stopPlay () {
    if (this.useRAF) {
      cancelAnimationFrame(this.animTimer)
      this.animTimer = null
    } else {
      clearInterval(this.animTimer)
      this.animTimer = null
    }
  }
  stopAllFrameAnimation () {
    this.stopLoop = true
    this._stopPlay()
  }
  /**
   * 順序播放
   * @param {Array} frameSections 動(dòng)畫(huà)幀片段
   */
  linearPlay (frameSections = this.frames) {
    return this._play(frameSections, this.currentIndex)
  }
  /**
   * 順序循環(huán)播放
   * @param {Array} frameSections 動(dòng)畫(huà)幀片段
   */
  loopPlay (frameSections = this.frames) {
    this._play(frameSections, this.currentIndex).then((res) => {
      if (!this.stopLoop) {
        this.currentIndex = 0
        this.loopPlay(frameSections, this.currentIndex)
      }
    })
  }
  // 倒序播放
  reversePlay (frameSections = this.frames) {
    frameSections.reverse()
    return this.linearPlay(frameSections)
  }
  // 倒序循環(huán)播放
  reverseLoopPlay (frameSections = this.frames) {
    frameSections.reverse()
    this.loopPlay(frameSections)
  }
  // 秋千式(單擺式)循環(huán)播放:即從第一幀播放到最后一幀,再由最后一幀播放到第一幀,如此循環(huán)
  swingLoopPlay (frameSections = this.frames) {
    this._play(frameSections, this.currentIndex).then((res) => {
      if (!this.stopLoop) {
        this.currentIndex = 0
        frameSections.reverse()
        this.swingLoopPlay(frameSections)
      }
    })
  }
  /**
   * 銷毀資源,需謹(jǐn)慎使用
   */
  disposeResource () {
    this.stopAllFrameAnimation()
    for (let i = 0; i < this.frames.length; i++) {
      this.frames[i] = null
    }
    this.frames = null
    this.canvas = this.ctx = null
  }
}

export default FrameAnim
CSS3 動(dòng)畫(huà)

css動(dòng)畫(huà)的原理是通過(guò)transition屬性或@keyframes/animation定義元素在動(dòng)畫(huà)中的關(guān)鍵幀,以實(shí)現(xiàn)漸變式的過(guò)渡。

css動(dòng)畫(huà)有以下特點(diǎn):

優(yōu)點(diǎn)

CSS動(dòng)畫(huà)實(shí)現(xiàn)比較簡(jiǎn)單

CSS動(dòng)畫(huà)執(zhí)行與JS主線程無(wú)關(guān),例如在Chromium里,css動(dòng)畫(huà)運(yùn)行在compositor thread線程中,即使你js線程卡住,css動(dòng)畫(huà)照常執(zhí)行

強(qiáng)制使用硬件加速,能有效利用GPU

缺點(diǎn)

只能操作DOM或XML對(duì)象的部分屬性

動(dòng)畫(huà)控制能力薄弱,不能逐幀定義動(dòng)畫(huà)狀態(tài)

支持的緩動(dòng)函數(shù)有限(CSS3動(dòng)畫(huà)的貝塞爾曲線是一個(gè)標(biāo)準(zhǔn)3次方曲線)

濫用硬件加速也會(huì)導(dǎo)致性能問(wèn)題

最新活動(dòng)
閱讀需要支付1元查看
<