const Sprite = require('./Sprite');
const { merge } = require('engine/utils/object');
/**
* @class AnimationData
*/
class AnimationData {
/**
* @constructor
* @constructor
* @param {Array} frames Frame data
* @param {Object} [props] Properties
*/
constructor(frames, props) {
/**
* Is animation looping.
* @property {Boolean} loop
* @default true
**/
this.loop = true;
/**
* Play animation in random order.
* @property {Boolean} random
* @default false
*/
this.random = false;
/**
* Play animation in reverse.
* @property {Boolean} reverse
* @default false
*/
this.reverse = false;
/**
* Speed of animation (frames per second).
* @property {Number} speed
* @default 10
*/
this.speed = 10;
/**
* Animation frame order.
* @property {Array} frames
*/
this.frames = frames;
merge(this, props);
}
}
/**
* @class AnimatedSprite
* @extends Sprite
*/
class AnimatedSprite extends Sprite {
/**
* @constructor
* @param {Array} textures Textures this animation made up of
*/
constructor(textures) {
super(textures[0]);
this.anims = {};
this.currentAnim = 'default';
this.currentFrame = 0;
this.isPlaying = false;
this.isFinished = false;
this._finishEvtEmit = false;
this._frameTime = 0;
this.textures = textures;
this.addAnim('default');
}
/**
* Remove from parent
* @method remove
* @memberof AnimatedSprite#
*/
remove() {
this.off('finish');
this.system.cancelAnimate(this);
super.remove();
}
/**
* Add new animation.
* @method addAnim
* @memberof AnimatedSprite#
* @param {String} name Name of the animation
* @param {Array} [frames] Frames list
* @param {Object} [props] Properties
* @return {AnimatedSprite} Self for chaining
*/
addAnim(name, frames, props) {
if (!name) {
return;
}
if (!frames) {
frames = [];
for (var i = 0; i < this.textures.length; i++) {
frames.push(i);
}
}
var anim = new AnimationData(frames, props);
this.anims[name] = anim;
return this;
}
/**
* Play animation.
* @method play
* @memberof AnimatedSprite#
* @param {String} name Name of animation
* @param {Number} [frame] Frame index
* @return {AnimatedSprite} Self for chaining
*/
play(name, frame = 0) {
name = name || this.currentAnim;
var anim = this.anims[name];
if (!anim) {
return;
}
this.isPlaying = true;
this._finishEvtEmit = false;
this.isFinished = false;
this.currentAnim = name;
if (!Number.isFinite(frame) && anim.reverse) {
frame = anim.frames.length - 1;
}
this.gotoFrame(frame);
this.system.requestAnimate(this);
return this;
}
/**
* Stop animation.
* @method stop
* @memberof AnimatedSprite#
* @param {Number} [frame] Frame index
* @return {AnimatedSprite} Self for chaining
*/
stop(frame) {
this.isPlaying = false;
if (Number.isFinite(frame)) {
this.gotoFrame(frame);
}
this.system.cancelAnimate(this);
return this;
}
/**
* Jump to specific frame.
* @method gotoFrame
* @memberof AnimatedSprite#
* @param {Number} frame
* @return {AnimatedSprite} Self for chaining
*/
gotoFrame(frame) {
var anim = this.anims[this.currentAnim];
if (!anim) {
return;
}
this.currentFrame = frame;
this._frameTime = 0;
this.texture = this.textures[anim.frames[frame]];
return this;
}
/**
* @memberof AnimatedSprite#
* @method update
* @memberof AnimatedSprite#
* @private
* @param {Number} delta Delta time since last frame.
*/
update(delta) {
var nextFrame;
var anim = this.anims[this.currentAnim];
if (this.isFinished) {
if (!this._finishEvtEmit) {
this.emit('finish', this.currentAnim);
}
return;
}
else if (this.isPlaying) {
this._frameTime += anim.speed * delta;
}
if (this._frameTime > 1000) {
this._frameTime -= 1000;
if (anim.random && anim.frames.length > 1) {
nextFrame = this.currentFrame;
while (nextFrame === this.currentFrame) {
nextFrame = Math.round(Math.random(0, anim.frames.length - 1));
}
this.currentFrame = nextFrame;
this.texture = this.textures[anim.frames[nextFrame]];
return;
}
nextFrame = this.currentFrame + (anim.reverse ? -1 : 1);
if (nextFrame >= anim.frames.length) {
if (anim.loop) {
this.currentFrame = 0;
this.texture = this.textures[anim.frames[0]];
}
else {
this.isPlaying = false;
this.isFinished = true;
this._finishEvtEmit = false;
}
}
else if (nextFrame < 0) {
if (anim.loop) {
this.currentFrame = anim.frames.length - 1;
this.texture = this.textures[anim.frames.last()];
}
else {
this.isPlaying = false;
this.isFinished = true;
this._finishEvtEmit = false;
this.system.cancelAnimate(this);
}
}
else {
this.currentFrame = nextFrame;
this.texture = this.textures[anim.frames[nextFrame]];
}
}
}
}
module.exports = AnimatedSprite;