从零到一实现一个原生js帧动画库(下)

69 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第14天,点击查看活动详情

上两篇文章实现了一个帧动画库,这一篇文章来演示如何使用。

利用webpack进行打包

现在src文件夹下面有三个文件,animation.js是主文件,imageloader.js用于图片的预加载,timeline.js定义了如何循环的执行动画函数。

animation.js

const loadImage = require('./imageloader')
const Timeline = require('./timeline')

function Animation() {}

module.exports = function () {
  return new Animation()
}

imageloader.js

function loadImage(images, callback, timeout) {}

module.exports = loadImage

timeline.js

function Timeline() {}

module.exports = Timeline

现在利用webpack打包,配置如下:

const path = require('path')
module.exports = {
  entry: {
    animation: './src/animation.js'
  },
  output: {
    path: path.resolve(__dirname, './build'),
    filename: '[name].js',
    // 挂载到window的名称
    library: 'animation',
    // 打包模式
    libraryTarget: 'umd'
  }
}

使用演示

代码如下:

<body>
    <div class="rabbit" id="rabbit1"></div>
    <div class="rabbit" id="rabbit2"></div>
    <div class="rabbit" id="rabbit3"></div>
    <div class="rabbit" id="rabbit4"></div>
    // 引入打包之后的文件
    <script src="../build/animation.js"></script>
    <script src="./demo.js"></script>
  </body>

普通使用

// demo.js

var images = ['./rabbit-big.png', './rabbit-lose.png', './rabbit-win.png']
var rightRunningMap = [ '0 -854', '-174 -852', ...]

function $(id) {
  return document.getElementById(id)
}
var rabbit1 = $('rabbit1')
var rabbit2 = $('rabbit2')
var rabbit3 = $('rabbit3')
var rabbit4 = $('rabbit4')

var animation = window.animation

function repeat() {
  var repeatAnimation = animation()
    .loadImage(images)
    .changePosition(rabbit1, rightRunningMap, images[0])
    .repeatForever()

  repeatAnimation.start(80)
}

repeat()

执行animation()返回一个animation类实例,通过loadImage, changePosition, repeatForever添加了三个任务,执行start把这三个任务跑起来。

chrome-capture-2022-6-4 (1).gif

兔子会一直奔跑,因为我们一直在改变图片的background-position。

高级使用

上面演示了帧动画库的一般使用,下面演示下帧动画库的高级使用,即自定义动画逻辑,动画效果如下:

chrome-capture-2022-6-4.gif

我们在源码中是这样定义的:

/**
 * 高级用法,添加一个异步定时任务,该任务自定义动画每帧执行的执行任务
 * @param taskFn 自定义每帧执行的任务函数
 */
Animation.prototype.enterFrame = function (taskFn) {
  return this._add(taskFn, TASK_ASYNC)
}

图中的动画是兔子先往右跑,然后往左跑,然后停顿1秒后做一个胜利的动画,对于这个动画,就不能仅仅改变background-position就能实现的了,需要我们自定义动画逻辑。

function run() {
  var speed = 6
  var initLeft = 100
  var finalLeft = 400
  var frameLength = 6
  var frame = 4
  var right = true
  var interval = 50

  var runAnimation = animation()
    .loadImage(images)
    .enterFrame(function (next, time) {
      var ratio = time / interval
      var position
      var left
      // 向右跑
      if (right) {
        position = rightRunningMap[frame].split(' ')
        left = Math.min(initLeft + ratio * speed, finalLeft)

        if (left === finalLeft) {
          right = false
          frame = 4
          next()
          return
        }
      } else {
        // 向左跑
        position = leftRunningMap[frame].split(' ')
        left = Math.max(initLeft, finalLeft - speed * ratio)

        if (left === initLeft) {
          right = true
          frame = 4
          next()
          return
        }
      }

      rabbit2.style.backgroundImage = 'url(' + images[0] + ')'
      rabbit2.style.backgroundPosition =
        position[0] + 'px ' + position[1] + 'px'
      rabbit2.style.left = left + 'px'
      frame++
      if (frame === frameLength) {
        frame = 0
      }
    })
    // 使其从左跑到右之后,再从右跑到左
    .repeat(1)
    .wait(1000)
    .changePosition(rabbit2, rabbitWinMap, images[2])
    .then(function () {
      console.log('finished')
    })

  runAnimation.start(interval)
}