2017-04-04 15:32:33 +00:00
|
|
|
var GetObjectValue = require('../../utils/object/GetObjectValue');
|
|
|
|
var GetFrames = require('./GetFrames');
|
|
|
|
|
2017-04-04 15:50:28 +00:00
|
|
|
var Animation = function (manager, key, config)
|
2017-04-04 15:32:33 +00:00
|
|
|
{
|
|
|
|
this.manager = manager;
|
|
|
|
|
2017-04-04 15:50:28 +00:00
|
|
|
this.key = key;
|
|
|
|
|
|
|
|
// Extract all the frame data into the frames array
|
2017-04-04 22:59:37 +00:00
|
|
|
this.frames = GetFrames(manager.textureManager, GetObjectValue(config, 'frames', []));
|
2017-04-04 15:32:33 +00:00
|
|
|
|
2017-04-05 00:15:53 +00:00
|
|
|
// The frame rate of playback in frames per second (default 24 if duration is null)
|
2017-04-06 23:07:20 +00:00
|
|
|
this.frameRate = GetObjectValue(config, 'framerate', null);
|
2017-04-04 15:32:33 +00:00
|
|
|
|
2017-04-06 23:07:20 +00:00
|
|
|
// How long the animation should play for. If frameRate is set it overrides this value
|
|
|
|
// otherwise frameRate is derived from duration
|
2017-04-04 15:32:33 +00:00
|
|
|
this.duration = GetObjectValue(config, 'duration', null);
|
|
|
|
|
2017-04-06 23:07:20 +00:00
|
|
|
if (this.duration === null && this.frameRate === null)
|
2017-04-04 15:32:33 +00:00
|
|
|
{
|
2017-04-06 23:07:20 +00:00
|
|
|
this.frameRate = 24;
|
|
|
|
this.duration = this.frameRate / this.frames.length;
|
2017-04-05 00:15:53 +00:00
|
|
|
}
|
2017-04-06 23:07:20 +00:00
|
|
|
else if (this.duration && this.frameRate === null)
|
2017-04-05 00:15:53 +00:00
|
|
|
{
|
2017-04-06 23:07:20 +00:00
|
|
|
// Duration given but no frameRate, so set the frameRate based on duration
|
2017-04-05 00:15:53 +00:00
|
|
|
// I.e. 12 frames in the animation, duration = 4 (4000 ms)
|
2017-04-06 23:07:20 +00:00
|
|
|
// So frameRate is 12 / 4 = 3 fps
|
|
|
|
this.frameRate = this.frames.length / this.duration;
|
2017-04-04 15:32:33 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-04-06 23:07:20 +00:00
|
|
|
// No duration, so derive from the frameRate
|
|
|
|
// I.e. 15 frames in the animation, frameRate = 30 fps
|
2017-04-05 00:15:53 +00:00
|
|
|
// So duration is 15 / 30 = 0.5 (half a second)
|
2017-04-06 23:07:20 +00:00
|
|
|
this.duration = this.frames.length / this.frameRate;
|
2017-04-04 15:32:33 +00:00
|
|
|
}
|
|
|
|
|
2017-04-05 00:15:53 +00:00
|
|
|
// ms per frame (without including frame specific modifiers)
|
2017-04-06 23:07:20 +00:00
|
|
|
this.msPerFrame = 1000 / this.frameRate;
|
2017-04-05 00:15:53 +00:00
|
|
|
|
|
|
|
// Skip frames if the time lags, or always advanced anyway?
|
2017-04-04 15:32:33 +00:00
|
|
|
this.skipMissedFrames = GetObjectValue(config, 'skipMissedFrames', true);
|
|
|
|
|
|
|
|
// Delay before starting playback (in seconds)
|
|
|
|
this.delay = GetObjectValue(config, 'delay', 0);
|
|
|
|
|
2017-04-05 00:15:53 +00:00
|
|
|
// Number of times to repeat the animation (-1 for infinity)
|
2017-04-04 15:32:33 +00:00
|
|
|
this.repeat = GetObjectValue(config, 'repeat', 0);
|
|
|
|
|
2017-04-05 00:15:53 +00:00
|
|
|
// Delay before the repeat starts (in seconds)
|
2017-04-04 15:32:33 +00:00
|
|
|
this.repeatDelay = GetObjectValue(config, 'repeatDelay', 0);
|
|
|
|
|
2017-04-05 00:15:53 +00:00
|
|
|
// Should the animation yoyo? (reverse back down to the start) before repeating?
|
2017-04-04 15:32:33 +00:00
|
|
|
this.yoyo = GetObjectValue(config, 'yoyo', false);
|
|
|
|
|
2017-04-10 13:38:44 +00:00
|
|
|
// Should sprite.visible = true when the animation starts to play?
|
|
|
|
this.showOnStart = GetObjectValue(config, 'showOnStart', false);
|
|
|
|
|
|
|
|
// Should sprite.visible = false when the animation finishes?
|
|
|
|
this.hideOnComplete = GetObjectValue(config, 'hideOnComplete', false);
|
|
|
|
|
2017-04-05 00:15:53 +00:00
|
|
|
// Callbacks (swap for Events?)
|
2017-04-04 15:32:33 +00:00
|
|
|
this.onStart = GetObjectValue(config, 'onStart', false);
|
|
|
|
this.onRepeat = GetObjectValue(config, 'onRepeat', false);
|
|
|
|
this.onComplete = GetObjectValue(config, 'onComplete', false);
|
|
|
|
this.onStop = GetObjectValue(config, 'onStop', false);
|
|
|
|
};
|
|
|
|
|
|
|
|
Animation.prototype.constructor = Animation;
|
|
|
|
|
|
|
|
Animation.prototype = {
|
|
|
|
|
2017-04-04 22:59:37 +00:00
|
|
|
load: function (component, startFrame)
|
2017-04-04 15:32:33 +00:00
|
|
|
{
|
2017-04-04 22:59:37 +00:00
|
|
|
if (startFrame >= this.frames.length)
|
|
|
|
{
|
|
|
|
startFrame = 0;
|
|
|
|
}
|
2017-04-04 15:32:33 +00:00
|
|
|
|
2017-04-06 23:07:20 +00:00
|
|
|
if (component.currentAnim !== this)
|
|
|
|
{
|
|
|
|
component.currentAnim = this;
|
|
|
|
|
2017-04-06 23:34:06 +00:00
|
|
|
component._timeScale = 1;
|
2017-04-06 23:07:20 +00:00
|
|
|
component.frameRate = this.frameRate;
|
|
|
|
component.duration = this.duration;
|
|
|
|
component.msPerFrame = this.msPerFrame;
|
|
|
|
component.skipMissedFrames = this.skipMissedFrames;
|
2017-04-06 23:29:20 +00:00
|
|
|
component._delay = this.delay;
|
|
|
|
component._repeat = this.repeat;
|
|
|
|
component._repeatDelay = this.repeatDelay;
|
|
|
|
component._yoyo = this.yoyo;
|
2017-04-06 23:07:20 +00:00
|
|
|
}
|
|
|
|
|
2017-04-05 01:06:28 +00:00
|
|
|
component.updateFrame(this.frames[startFrame]);
|
2017-04-04 15:32:33 +00:00
|
|
|
},
|
|
|
|
|
2017-04-04 22:59:37 +00:00
|
|
|
checkFrame: function (index)
|
2017-04-04 15:32:33 +00:00
|
|
|
{
|
2017-04-04 22:59:37 +00:00
|
|
|
return (index < this.frames.length);
|
2017-04-04 15:32:33 +00:00
|
|
|
},
|
|
|
|
|
2017-04-05 23:58:48 +00:00
|
|
|
getFirstTick: function (component, includeDelay)
|
2017-04-05 03:18:08 +00:00
|
|
|
{
|
2017-04-05 23:58:48 +00:00
|
|
|
if (includeDelay === undefined) { includeDelay = true; }
|
|
|
|
|
2017-04-05 03:18:08 +00:00
|
|
|
// When is the first update due?
|
|
|
|
component.accumulator = 0;
|
2017-04-06 23:07:20 +00:00
|
|
|
component.nextTick = component.msPerFrame + component.currentFrame.duration;
|
2017-04-05 23:58:48 +00:00
|
|
|
|
|
|
|
if (includeDelay)
|
|
|
|
{
|
2017-04-06 23:29:20 +00:00
|
|
|
component.nextTick += (component._delay * 1000);
|
2017-04-05 23:58:48 +00:00
|
|
|
}
|
2017-04-05 03:18:08 +00:00
|
|
|
},
|
|
|
|
|
2017-04-05 00:15:53 +00:00
|
|
|
getNextTick: function (component)
|
|
|
|
{
|
|
|
|
// When is the next update due?
|
2017-04-05 02:22:54 +00:00
|
|
|
component.accumulator -= component.nextTick;
|
2017-04-06 23:07:20 +00:00
|
|
|
component.nextTick = component.msPerFrame + component.currentFrame.duration;
|
2017-04-05 00:15:53 +00:00
|
|
|
},
|
|
|
|
|
2017-04-05 01:06:28 +00:00
|
|
|
nextFrame: function (component)
|
2017-04-04 15:32:33 +00:00
|
|
|
{
|
2017-04-05 00:15:53 +00:00
|
|
|
var frame = component.currentFrame;
|
|
|
|
|
2017-04-05 01:06:28 +00:00
|
|
|
// TODO: Add frame skip support
|
2017-04-05 00:15:53 +00:00
|
|
|
|
2017-04-05 03:18:08 +00:00
|
|
|
if (frame.isLast)
|
2017-04-05 01:06:28 +00:00
|
|
|
{
|
|
|
|
// We're at the end of the animation
|
|
|
|
|
|
|
|
// Yoyo? (happens before repeat)
|
|
|
|
if (this.yoyo)
|
|
|
|
{
|
|
|
|
component.forward = false;
|
2017-04-06 23:07:20 +00:00
|
|
|
|
|
|
|
component.updateFrame(frame.prevFrame);
|
2017-04-05 02:22:54 +00:00
|
|
|
|
2017-04-05 03:18:08 +00:00
|
|
|
// Delay for the current frame
|
2017-04-05 02:22:54 +00:00
|
|
|
this.getNextTick(component);
|
2017-04-05 01:06:28 +00:00
|
|
|
}
|
|
|
|
else if (component.repeatCounter > 0)
|
|
|
|
{
|
|
|
|
// Repeat (happens before complete)
|
|
|
|
this.repeatAnimation(component);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-04-10 13:38:44 +00:00
|
|
|
this.completeAnimation(component);
|
2017-04-05 01:06:28 +00:00
|
|
|
}
|
|
|
|
}
|
2017-04-05 02:22:54 +00:00
|
|
|
else
|
|
|
|
{
|
2017-04-05 03:18:08 +00:00
|
|
|
component.updateFrame(frame.nextFrame);
|
2017-04-04 22:59:37 +00:00
|
|
|
|
2017-04-05 02:22:54 +00:00
|
|
|
this.getNextTick(component);
|
|
|
|
}
|
2017-04-05 01:06:28 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
previousFrame: function (component)
|
|
|
|
{
|
|
|
|
var frame = component.currentFrame;
|
|
|
|
|
|
|
|
// TODO: Add frame skip support
|
|
|
|
|
2017-04-05 03:18:08 +00:00
|
|
|
if (frame.isFirst)
|
2017-04-05 00:15:53 +00:00
|
|
|
{
|
2017-04-05 01:06:28 +00:00
|
|
|
// We're at the start of the animation
|
|
|
|
|
|
|
|
if (component.repeatCounter > 0)
|
|
|
|
{
|
|
|
|
// Repeat (happens before complete)
|
|
|
|
this.repeatAnimation(component);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-04-10 13:38:44 +00:00
|
|
|
this.completeAnimation(component);
|
2017-04-05 01:06:28 +00:00
|
|
|
}
|
|
|
|
}
|
2017-04-05 03:18:08 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
component.updateFrame(frame.prevFrame);
|
|
|
|
|
|
|
|
this.getNextTick(component);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
repeatAnimation: function (component)
|
|
|
|
{
|
2017-04-06 23:29:20 +00:00
|
|
|
if (component._repeatDelay > 0 && component.pendingRepeat === false)
|
2017-04-05 03:18:08 +00:00
|
|
|
{
|
|
|
|
component.pendingRepeat = true;
|
|
|
|
component.accumulator -= component.nextTick;
|
2017-04-06 23:29:20 +00:00
|
|
|
component.nextTick += (component._repeatDelay * 1000);
|
2017-04-05 03:18:08 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
component.repeatCounter--;
|
|
|
|
|
|
|
|
component.forward = true;
|
|
|
|
|
|
|
|
component.updateFrame(component.currentFrame.nextFrame);
|
|
|
|
|
|
|
|
this.getNextTick(component);
|
|
|
|
|
|
|
|
component.pendingRepeat = false;
|
|
|
|
|
|
|
|
// OnRepeat
|
|
|
|
}
|
2017-04-05 01:06:28 +00:00
|
|
|
},
|
|
|
|
|
2017-04-10 13:38:44 +00:00
|
|
|
completeAnimation: function (component)
|
|
|
|
{
|
|
|
|
component.stop();
|
|
|
|
|
|
|
|
if (this.hideOnComplete)
|
|
|
|
{
|
|
|
|
component.parent.visible = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Events
|
|
|
|
|
|
|
|
},
|
|
|
|
|
2017-04-05 01:06:28 +00:00
|
|
|
setFrame: function (component)
|
|
|
|
{
|
|
|
|
// Work out which frame should be set next on the child, and set it
|
|
|
|
if (component.forward)
|
|
|
|
{
|
|
|
|
this.nextFrame(component);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.previousFrame(component);
|
2017-04-05 00:15:53 +00:00
|
|
|
}
|
2017-04-04 15:32:33 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
destroy: function ()
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
module.exports = Animation;
|