内容字号:默认大号超大号

段落设置:段首缩进取消段首缩进

字体设置:切换到微软雅黑切换到宋体

一起动手实现一个js帧动画库

2018-08-04 18:18 出处:清屏网 人气: 评论(0

就是通过一张图片切换background Position,或者通过多张图片的切换src来进行的动画效果

相当一部分的浏览器的显示频率是16.7ms,所以在进行帧动画的时候选用16.7ms的频率或者是16.7ms的倍数,避免丢帧。所以我们采用requestAnimationFrame来执行动画的操作。

动画库的接口设计

animation(imgList) // animation 类

对外暴露接口:

changePosition(ele, positions, imageUrl) // 改变元素的 background-position 实现帧动画

changeUrl(ele, imgList) // 通过改变图片元素的URL 实现帧动画

repeat(times) // 动画执行的次数 times为空时无限循环

wait(time) // 动画的等待时间

then(callback) // 自定义执行任务

start(interval) // 动画开始执行,interval 表示动画执行的间隔 默认为16.7ms

pause() // 动画暂停

restart() // 动画从上一次暂停处重新执行

私有接口:

_loadImage(imgList) // 预加载图片

_add(taskFn, type) // 方法添加到任务队列中,taskFn为任务函数,type为任务类型

_runTask() // 执行任务

_next() // 当前任务结束后执行下一个任务

_dispose() // 释放资源

将所有的接口先定义完成

var TIMING = 1000 / 60 // 一秒60帧的执行速度和浏览器的显示速度相同
/**
 * 帧动画类
 */
function Animation (imgList) {
  /**
   * 0为初始状态
   * 1为运动状态
   * 2为暂停状态
   */
  this._state = 0
  // 任务队列,所有事件都被载入这个任务队列中,在start后被依次执行
  this._taskQuery = []
  // 执行当前任务的索引
  this._index = 0
  // 执行任务前要先加载好img后才能执行下一个任,避免动画执行中img还没加载出来
  this._loadImage(imgList)
}

/**
 * 改变元素的 background-position 实现帧动画
 * @param ele dom对象
 * @param positions 背景位置数组 示例: ['20 0', '40 0']
 * @param imageUrl 背景图片url
 */
Animation.prototype.changePosition = function (ele, positions, imageUrl) {
  return this
}

/**
 * 通过改变图片元素的URL 实现帧动画
 * @param ele dom对象
 * @param imglist 图片url数组
 */
Animation.prototype.changeUrl = function (ele, imglist) {
  return this
}

/**
 * 循环执行
 * @param times 循环执行上一个任务的次数
 */
Animation.prototype.repeat = function (times) {
  return this
}

/**
 * 执行下一个任务的等待时长
 * @param time 等待的时长
 */
Animation.prototype.wait = function (time) {
  return this
}

/**
 * 自定义执行任务
 * @param calback 自定义执行任务
 */
Animation.prototype.then = function (calback) {
  return this
}

/**
 * 动画开始执行
 * @param interval 动画执行的频率
 */
Animation.prototype.start = function (interval) {
  this.interval = interval || TIMING
  return this
}

/**
 * 动画暂停
 */
Animation.prototype.pause = function () {
  return this
}

/**
 * 动画从上一次暂停处重新执行
 */
Animation.prototype.restart = function() {
  return this
}


/**
 * 图片预加载
 * @param imgList 预加载的图片数组
 */
Animation.prototype._loadImage = function (imgList) {
}

/**
 * 添加任务到任务队列
 * @param taskFn 执行的任务
 * @param type 任务类型
 */
Animation.prototype._add = function(taskFn, type) {

}

/**
 * 执行当前任务
 */
Animation.prototype._runTask = function () {

}

/**
 * 切换到下一个任务
 */
Animation.prototype._next = function () {

}

/**
 * 释放资源
 */
Animation.prototype._dispose = function () {

}
复制代码

接下来我们来实现_loadImage

Animation.prototype._loadImage = function (imgList) {
// 每个taskFn都会接受一个next参数,在_runTask中执行taskFn的时候会传入,用来在任务执行完成后进行下一个操作
  var taskFn = function (next) {
    // 图片加载事件
    loadImage(imgList, next)
  }
  /**
   * 0为非动画任务
   * 1为动画任务 比如 changePosition 和 changeUrl 事件
   */
  var type = 0

  this._add(taskFn, type)
}
复制代码

这里出现了我们未曾定义的事件loadImage,我们新建一个loadimage.js, 把loadinimage引入当前js中吧 var loadImage = require('./imageLoad') 。 不过这个事件要待会来实现,我们先把简单的事先做了吧。

我们先吧这里用到的_add先实现把,顺便把_next也实现一下把

/**
 * 添加任务到任务队列
 * @param taskFn 执行的任务
 * @param type 任务类型
 */
Animation.prototype._add = function(taskFn, type) {
  this._taskQuery.push({
    taskFn: taskFn,
    type: type
  })
}
/**
 * 切换到下一个任务
 */
Animation.prototype._next = function () {
  this._index++
  this._runTask()
}
复制代码

是不是很简单。看这里又用到了_runTask,接下来我们分析一下这个函数吧。

任务中分为两种任务,一个是非动画任务,一个是动画任务。 那么我们就需要先创建两个方法。在_runTask中判断任务类型来执行对应的方法。 _syncTask和_asyncTask方法。

/**
 * 动画任务
 * @param task 任务对象 {taskFn, type}
 */
Animation.prototype._asyncTask = function (task) {

}

/**
 * 非动画任务
 * @param task 任务对象 {taskFn, type}
 */
Animation.prototype._syncTask = function (task) {

}
复制代码

好了我们接下来先看看_runTask的逻辑吧

/**
 * 执行当前任务
 */
Animation.prototype._runTask = function () {
  // 当任务队列没有任务时或者当前不是处于运动状态时就不做任何操作
  if (!this._taskQuery || this.state !== 1) {
    return
  }

  // 当任务全部完成时释放资源
  if (this._index === this._taskQuery.length) {
    this._dispose()
    return
  }

  var task = this._taskQuery[this._index]
  var type = task.type
  if (type === 0) {
    this._syncTask(task)
  } else if (type === 1) {
    this._asyncTask(task)
  }
}
复制代码

我们这里用到了_dispose释放资源,这个当然是最好才写的一个方法,_asyncTask和_syncTask当然那最简单的先来练练手。 我们先来看看_syncTask非动画任务来试试

/**
 * 非动画任务
 * @param task 任务对象 {taskFn, type}
 */
Animation.prototype._syncTask = function (task) {
  var _this = this
  var next = function () {
    _this._next()
  }
  var taskFn = task.taskFn
  taskFn(next)
}
复制代码

现在写了那么多next有些小伙伴可能有点蒙圈了,怎么都没用过next呀,我有点跑不通逻辑呀。那么我们先来写一个非动画任务then方法来试试

/**
 * 自定义执行任务
 * @param calback 自定义执行任务
 */
Animation.prototype.then = function (calback) {
// 这里的taskFn接受一个next参数
  var taskFn = function (next) {
    calback()
    next() //taskFn在_runTask中被调用了,这里的next就是在_runTask方法中传入的_next方法中
  }
  var type = 0
  // 在_add方法中我们会接受taskFn方法
  this._add(taskFn, type)
  return this
}
复制代码

看到我们的我们的then方法了吗。我来给大家来理一下next吧。其实在_add方法中我们会接受taskFn方法,这里的taskFn又接受一个next参数,taskFn会在_runTask方法中被调用就会传入_next方法那就是这里的next

那么_next方法讲明白了,那我们就开始讲repeat(times)方法吧 // 重复执行上一个任务

/**
 * 循环执行
 * @param times 循环执行上一个任务的次数
 */
Animation.prototype.repeat = function (times) {
  var _this = this
  var taskFn = function (next) {
    // times为空 无限循环上一个任务
    if (typeof times === 'undefined') {
      _this._index--
      _this._runTask()
      return
    }
    // times 有数值时
    if (times) {
      times--
      _this._index--
      _this._runTask()
      return
    } else {
      next()
    }
  }
  var type = 0
  this._add(taskFn, type)
  return this
}
复制代码

接下来我们在来看看wait方法和start方法吧

/**
 * 执行下一个任务的等待时长
 * @param time 等待的时长
 */
Animation.prototype.wait = function (time) {
  var taskFn = function (next) {
    setTimeout(function () {
      next()
    }, time);
  }
  var type = 0
  this._add(taskFn, type)
  return this
}

/**
 * 动画开始执行
 * @param interval 动画执行的频率
 */
Animation.prototype.start = function (interval) {
  // 本身已经是执行状态或者任务队列里没有任务时不进行操作
  if (this.state === 1 || !this.taskQuery.length) {
    return this
  }
  
  this.interval = interval || TIMING
  this.state = 1
  this._runTask()
  return this
}
复制代码

分享给小伙伴们:
本文标签: 动画

相关文章

发表评论愿您的每句评论,都能给大家的生活添色彩,带来共鸣,带来思索,带来快乐。

CopyRight © 2015-2016 QingPingShan.com , All Rights Reserved.

清屏网 版权所有 豫ICP备15026204号