-
Notifications
You must be signed in to change notification settings - Fork 0
/
animatedsprite.js
186 lines (174 loc) · 6.71 KB
/
animatedsprite.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
'use strict';
/**
* An object for storing animations.
* @param {Object} animationData Data for animation frames. Keys are animation ids.
* Values are arrays containing objects specifying frames.
* Each frame has two mandatory keys: 'src' for frame source and 'duration' for a duration in milliseconds.
* Duration can be set to 0 to have the frame run into infinity.
* Example:
* {
* idle: [{src: 'idle.png', duration: 0}],
* walk: [{src: 'walk1.png', duration: 50}, {src: 'walk2.png', duration: 50}]
* }
* @param {Object} options Object with the following optional keys:
* frameConstructor: function Constructor for single frames that takes the
* frame source as a parameter. Defaults to AnimatedSprite.frameConstructor.
* durationMultiplier: number Multiplier for specified frame durations. Useful if you
* want to have frame times relative to fixed FPS, for example. Defaults to 1.
* defaultDuration: number Default duration for a single frame. Defaults to 1.
*/
var AnimatedSprite = function(animationData, options) {
var defaults = {
frameConstructor: AnimatedSprite.frameConstructor,
durationMultiplier: 1,
defaultDuration: 1
};
for(var key in defaults) {
if (!options.hasOwnProperty(key)) {
this[key] = defaults[key];
} else {
this[key] = options[key];
}
}
// Construct animations by generating animation frames based on the sources.
this.animations = {};
this.defaultAnimation = undefined;
for (var key in animationData) {
if (animationData.hasOwnProperty(key)) {
var animation = [];
var singleAnimationData = animationData[key];
for (var i = 0; i < singleAnimationData.length; ++i) {
var frame = AnimatedSprite._getFrame(singleAnimationData[i].src, this.frameConstructor);
var duration = this.defaultDuration;
if (singleAnimationData[i].duration !== undefined)
{
duration = singleAnimationData[i].duration
}
duration *= this.durationMultiplier;
animation.push({frame: frame, duration: duration});
}
this.animations[key] = animation;
if (this.defaultAnimation === undefined) {
this.defaultAnimation = key;
}
}
}
};
/**
* Default constructor for single frames. Set this before loading any animations.
*/
AnimatedSprite.frameConstructor = null;
if ('Sprite' in window) {
AnimatedSprite.frameConstructor = Sprite;
}
AnimatedSprite._getFrame = (function() {
var frameCaches = [];
return (function(src, frameConstructor) {
var cachedFrames;
for (var j = 0; j < frameCaches.length; ++j) {
if (frameCaches[j].frameConstructor === frameConstructor) {
cachedFrames = frameCaches[j].cachedFrames;
break;
}
}
if (cachedFrames === undefined) {
cachedFrames = {};
frameCaches.push({frameConstructor: frameConstructor, cachedFrames: cachedFrames});
}
if (!cachedFrames.hasOwnProperty('_' + src)) {
var frame = new frameConstructor(src);
cachedFrames['_' + src] = frame;
}
return cachedFrames['_' + src];
});
})();
/**
* An object that stores the current state of an animated sprite.
* @param {AnimatedSprite} animatedSprite The animated sprite to use.
* @param {function=} finishedFrameCallback A callback to execute when an animation has finished. Can be used to
* switch to a different animation, for example. Takes the finished animation key as a parameter.
*/
var AnimatedSpriteInstance = function(animatedSprite, finishedAnimationCallback) {
this.animatedSprite = animatedSprite;
this.finishedAnimationCallback = finishedAnimationCallback;
this.setAnimation(this.animatedSprite.defaultAnimation);
var frame = this.animatedSprite.animations[this.animationKey][this.frame].frame;
// Add draw functions dynamically - this is a bit inefficient, but makes this class very generic.
var that = this;
for (var key in frame) {
if ((typeof(frame[key]) === 'function') && key.substr(0, 4) === 'draw') {
(function(drawFuncName) {
that[drawFuncName] = function() {
var frame = that.getCurrentFrame();
frame[drawFuncName].apply(frame, arguments);
}
})(key);
}
}
};
/**
* Start playing an animation.
* @param {string} animationKey The animation id in the AnimatedSprite.
*/
AnimatedSpriteInstance.prototype.setAnimation = function(animationKey) {
this.animationKey = animationKey;
this.frame = 0;
this.framePos = 0;
};
/**
* Update the current animation frame.
* @param {number} deltaTime Time that has passed since the last update.
*/
AnimatedSpriteInstance.prototype.update = function(deltaTime) {
this._scrubInternal(deltaTime, this.finishedAnimationCallback);
};
/**
* Scrub the animation backwards or forwards.
* @param {number} deltaTime Amount to scrub by.
*/
AnimatedSpriteInstance.prototype.scrub = function(deltaTime) {
this._scrubInternal(deltaTime);
};
AnimatedSpriteInstance.prototype._scrubInternal = function(deltaTime, finishCallback) {
var currentAnimation = this.animatedSprite.animations[this.animationKey];
if (currentAnimation[this.frame].duration > 0) {
this.framePos += deltaTime * 1000;
while (this.framePos > currentAnimation[this.frame].duration) {
this.framePos -= currentAnimation[this.frame].duration;
++this.frame;
if (this.frame >= currentAnimation.length) {
this.frame = 0;
if (finishCallback !== undefined) {
finishCallback(this.animationKey);
}
}
if (currentAnimation[this.frame].duration <= 0) {
this.framePos = 0.0;
return;
}
}
while (this.framePos < 0) {
--this.frame;
if (this.frame < 0) {
this.frame = currentAnimation.length - 1;
}
this.framePos += currentAnimation[this.frame].duration;
if (currentAnimation[this.frame].duration <= 0) {
this.framePos = 0.0;
return;
}
}
}
};
/**
* @return {string} The current animation key.
*/
AnimatedSpriteInstance.prototype.getCurrentAnimation = function() {
return this.animationKey;
};
/**
* @return {Object} The current frame of the animation.
*/
AnimatedSpriteInstance.prototype.getCurrentFrame = function() {
return this.animatedSprite.animations[this.animationKey][this.frame].frame;
};