"use strict"
var loadImage = require("./imageloader");
var Timeline = require("./timeline");
var STATE_INITIAL = 0;
var STATE_START = 1;
var STATE_STOP = 2;
var STATE_SYNC = 0;
var STATE_ASYNC = 1;
* @说明:执行callback
* @param callback 执行的函数
*/
function next(callback) {
callback && callback()
}
* @类名:帧动画
* @constructor
*/
function Animation() {
this.taskQueue = [];
this.index = 0;
this.state = STATE_INITIAL;
this.timeline = new Timeline();
}
* @接口说明:添加一个同步任务,预加载图片
* @param imglist 图片数组
*/
Animation.prototype.loadImage = function(imglist) {
var taskFn = function(next) {
loadImage(imglist.slice(), next);
};
var type = TASK_SYNC;
return this._add(taskFn, type);
}
* @接口说明:添加一个异步定时任务,通过改变图片背景位置实现帧动画
* @param ele DOM对象
*/
Animation.prototype.changePosition = function(ele, positions, imageUrl) {
var len = positions.length;
var taskFn;
var type;
if(len){
taskFn = function(next, time) {
if(imageUrl) {
ele.style.backgroundImage = "url(" + imageUrl + ")";
}
var index = Math.min(time / _this.interval | 0, len - 1);
var position = positions[index].split(" ");
ele.style.backgroundPosition = position[0] + "px " + position[1] + "px";
};
type = TASK_ASYNC;
} else {
taskFn = next;
type = TASK_SYNC;
}
return this._add(taskFn, type);
}
* @接口说明: 添加一个异步定时任务,通过定时改变image标签的src属性,实现帧动画
* @param ele image标签
* @param imglist 图片数组
*/
Animation.prototype.changeSrc = function(ele, imglist) {
var len = imglist.length;
var taskFn;
var type;
if(len) {
var _this = this;
taskFn = function(next, time) {
var index = Math.min(time / _this.interval | 0, len - 1);
ele.src = imglist[index];
if(index === len-1) {
next();
}
}
type = TASK_ASYNC;
} else {
taskFn = next;
type = TASK_SYNC;
}
return this._add(taskFn, type);
}
* @接口说明:添加一个异步定时执行的任务,该任务自定义动画每帧执行的任务函数
* @param taskFn 自定义每帧执行的任务函数
*/
Animation.prototype.enterFrame = function(taskFn) {
return this._add(taskFn, TASK_ASYNC);
}
* @接口说明:添加一个同步任务,可以在上一个任务完成后执行回调函数
* @param callback 回调函数
*/
Animation.prototype.then = function(callback) {
var taskFn = function(next) {
callback;
next();
}
var type = TASK_SYNC;
return this._add(taskFn, type);
}
* @接口说明:开始执行函数, 异步定义任务执行的间隔
* @param interval
*/
Animation.prototype.start = function(interval) {
if(this.state === STATE_START) {
return this;
}
if(!this.taskQueue.length) {
return this;
}
this.state = STATE_START;
this.interval = interval;
this._runTask();
return this;
}
* @接口说明:添加一个同步任务,回退到上一个任务中,实现重复上一个任务的效果,可以自定义次数
* @param times 重复次数
*/
Animation.prototype.repeat = function(times) {
var _this = this;
var taskFn = function() {
if(typeof times === "underfined") {
_this.index--;
_this._runTask();
return;
}
if(times) {
times--;
_this.index--;
_this._runTask()
} else {
var task = _this.taskQueue[_this.index];
_this._next(task);
}
}
var type = TASK_SYNC;
return this.__add(taskFn, type);
}
* @接口说明:添加一个同步任务,相当于repeat()更友好的接口,无限循环上一次任务
*
*/
Animation.prototype.repeatForever = function() {
return this.repeat()
}
* @接口说明:设置当前任务执行结束后,到下个任务开始前的等待时间
* @param time 等待时长
*/
Animation.prototype.wait = function(time) {
if(this.taskQueue && this.taskQueue.length > 0){
this.taskQueue[this.taskQueue.length - 1] = time;
}
return this;
}
* @接口说明:暂停当前异步定时任务
*/
Animation.prototype.pause = function(time) {
if(this.state === STATE_START) {
this.state = STATE_STOP;
this.timeline.stop();
}
return this;
}
* @接口说明:重新执行上一次暂停的异步任务
*/
Animation.prototype.restart = function() {
if(this.state === STATE_STOP) {
this.state = STATE_START;
this.timeline.restart();
return this;
}
return this;
}
* @接口说明:释放资源
*/
Animation.prototype.dispose = function() {
if(this.state !== STATE_INITIAL) {
this.state = STATE_INITIAL;
this.taskQueue = null;
this.timeline.stop();
this.timeline = null;
return this;
}
return this;
}
* @接口说明:添加一个任务到队列中
* @param taskFn 任务方法
* @param type 任务类型
* @private
*/
Animation.prototype._add = function(taskFn, type) {
this.taskQueue.push({
taskFn: taskFn,
type: type
})
}
* @接口说明:执行任务
* @private
*/
Animation.prototype._runTask = function() {
if(!this.taskQueue || this.state !== STATE_START) {
return;
}
if(this.index === this.taskQueue.length) {
this.dispose()
return;
}
var task = this.taskQueue[this.index];
if(task.type === TASK_SYNC){
this._syncTask(task);
} else {
this._asyncTask(task);
}
}
* @接口说明:同步任务
* @param task 执行的任务对象
* @private
*/
Animation.prototype._syncTask = function(task) {
var _this = this;
var next = function() {
_this._next(task)
};
var taskFn = task.taskFn;
taskFn(next);
}
* @接口说明:异步任务
* @param task 执行的任务对象
* @private
*/
Animation.prototype._asyncTask = function(task) {
var _this = this;
var enterFrame = function(time) {
var taskFn = task.taskFn;
var next = function() {
_this.timeline.stop()
_this._next(task)
}
taskFn(next, time);
}
this.timeline.onenterframe = enterFrame;
this.timeline.start(this.interval);
}
* @接口说明:切换到下一个任务,支持如果当前任务需要等待,则延时执行
* @param task 当前任务
* @private
*/
Animation.prototype._next = function() {
this.index++;
var _this = this;
task.wait ? setTimeout(function() {
_this._runTask();
}, task.wait) : this._runTask();
}
module.exports = function() {
return new Animation();
}
"use strict";
* 预加载图片函数
* @param images 加载图片的数组或对象
* @param callback 全部资源加载完成后的回调
* @param timeout 加载超时的时长
*/
function loadImage(images, callback, timeout) {
var count = 0;
var success = true;
var timeoutId = 0;
var isTimeout = false;
for(var key in images) {
if(!images.hasOwnProperty(key)) {
continue;
}
var item = images[key];
if(typeof item === "string") {
item = images[key] = {
src:item
};
}
if(!item || !item.src) {
continue;
}
count++;
item.id = "__img__" + key + getId();
// 设置图片元素的 img,它是一个 image 对象
iten.img = window[item.id] = new Image();
doLoad(item);
}
// 遍历完成,如果计数为0,则直接调用callback
if(!count){
callback(success);
} else if(timeout){
timeoutId = setTimeou(onTimeout, timeout);
}
/**
* @接口说明:真正进行图片加载的函数
* @param item 图片元素对象
*/
function doLoad(item) {
item.status = "loading";
var img = item.img;
id = item.id
img.onload = function() {
success = success & true;
item.status = "loaded";
done();
}
img.onerror = function() {
success = false;
item.status = "error";
done();
}
img.src = item.src;
* 每张图片加载成功的回调函数
*/
function done() {
img.onload = img.onerror = null;
try{
delete window[item.id];
} catch (e) {
}
if(!--count && !isTimeout) {
clearTimeout(timeoutId);
callback(success);
}
}
}
function onTimeout() {
isTimeout = true;
callback(false);
}
}
var __id = 0;
function getId() {
return ++__id;
}
module.exports = loadImage;
"use strict"
var DEFAULT_INTERVAL = 1000 / 60;
var STATE_INITIAL = 0;
var STATE_START = 1;
var STATE_STOP = 2;
var requestAnimationFrame = (function() {
return window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
function (callback) {
return window.setTimeout(callback, callback.interval || DEFAULT_INTERVAL)
}
})();
var cancelAnimationFrame = (function() {
return window.cancelAnimationFrame ||
window.webkitCancelRequestAnimationFrame ||
window.mozCancelRequestAnimationFrame ||
window.oCancelRequestAnimationFrame ||
function(id) {
return window.clearTimeout(id);
}
})();
* @类名:Timeline 时间轴类
* @contructor
*/
function Timeline() {
this.animationHandler = 0;
this.state = STATE_INITIAL;
}
* @接口说明:时间轴上每一次回调执行的函数
* @param time 从动画开始到当前执行的时间
*/
Timeline.prototype.onenterframe = function(item) {
};
* @接口说明:动画开始
* @param interval 每一次回调的间隔时间
*/
Timeline.prototype.start = function() {
if(this.state === STATE_START) {
return;
}
this.state = STATE_START;
this.interval = interval || DEFAULT_INTERVAL;
startTime(this, +new Date());
};
* @接口说明:动画停止
*/
Timeline.prototype.stop = function() {
if(this.state !== STATE_STOP) {
return;
}
this.state = STATE_STOP;
if(this.startTime) {
this.dur = +new Date() - this.startTime;
}
cancelAnimationFrame(this.animationHandler);
}
* @接口说明:重新开始动画
*/
Timeline.prototype.restart = function() {
if(this.state === STATE_START) {
return;
}
if(!this.dur || !this.interval) {
return;
}
this.state = STATE_START;
startTimeline(this, +new Date() - this.dur);
}
* @接口说明:时间轴动画启动函数
* @param timeline 时间轴的实例
* @param startTime 动画开始时间戳
*/
function startTimeline(timeline, startTime) {
timeline.startTime = startTime;
nextTick.interval = timeline.interval;
var lastTick = +new Date();
nextTick();
* @说明:每一帧执行的函数
*/
function nextTick() {
var now = +new Date();
timeline.animationHandler = requestAnimationFrame(nextTick);
if(now - lastTick >= timeline.interval) {
timeline.onenterFrame(now - startTime);
lastTick = now;
}
}
}
真正使用的时候,将animation.js中require其他两个文件imageloader.js和timeline.js。
<div id="test"></div>
var animation = require("frame-animation");
var ele = document.getElementById('test');
var frameMap = ['0 0', '0 -100', '0 -200'];
var demoAnimation = animation().changePosition(ele, positions).repeat();
demoAnimation.start(200);
评论