phaser/v3/src/animation/frame/Animation.js

288 lines
8.4 KiB
JavaScript
Raw Normal View History

var GetObjectValue = require('../../utils/object/GetObjectValue');
var GetFrames = require('./GetFrames');
2017-04-04 15:50:28 +00:00
var Animation = function (manager, key, config)
{
this.manager = manager;
2017-04-04 15:50:28 +00:00
this.key = key;
// Extract all the frame data into the frames array
this.frames = GetFrames(manager.textureManager, GetObjectValue(config, 'frames', []));
2017-04-05 00:15:53 +00:00
// The frame rate of playback in frames per second (default 24 if duration is null)
this.frameRate = GetObjectValue(config, 'framerate', null);
// How long the animation should play for. If frameRate is set it overrides this value
// otherwise frameRate is derived from duration
this.duration = GetObjectValue(config, 'duration', null);
if (this.duration === null && this.frameRate === null)
{
// No duration or frameRate given, use default frameRate of 24fps
this.frameRate = 24;
this.duration = this.frameRate / this.frames.length;
2017-04-05 00:15:53 +00:00
}
else if (this.duration && this.frameRate === null)
2017-04-05 00:15:53 +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)
// So frameRate is 12 / 4 = 3 fps
this.frameRate = this.frames.length / this.duration;
}
else
{
// frameRate given, derive duration from it (even if duration also specified)
// 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)
this.duration = this.frames.length / this.frameRate;
}
2017-04-05 00:15:53 +00:00
// ms per frame (without including frame specific modifiers)
this.msPerFrame = 1000 / this.frameRate;
2017-04-05 00:15:53 +00:00
// Skip frames if the time lags, or always advanced anyway?
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)
this.repeat = GetObjectValue(config, 'repeat', 0);
2017-04-05 00:15:53 +00:00
// Delay before the repeat starts (in seconds)
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?
this.yoyo = GetObjectValue(config, 'yoyo', false);
// 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);
// Callbacks
this.callbackScope = GetObjectValue(config, 'callbackScope', this);
this.onStart = GetObjectValue(config, 'onStart', false);
this.onStartParams = GetObjectValue(config, 'onStartParams', []);
this.onRepeat = GetObjectValue(config, 'onRepeat', false);
this.onRepeatParams = GetObjectValue(config, 'onRepeatParams', []);
2017-04-10 16:04:24 +00:00
// Called for EVERY frame of the animation.
// See AnimationFrame.onUpdate for a frame specific callback.
this.onUpdate = GetObjectValue(config, 'onUpdate', false);
this.onUpdateParams = GetObjectValue(config, 'onUpdateParams', []);
this.onComplete = GetObjectValue(config, 'onComplete', false);
this.onCompleteParams = GetObjectValue(config, 'onCompleteParams', []);
};
Animation.prototype.constructor = Animation;
Animation.prototype = {
load: function (component, startFrame)
{
if (startFrame >= this.frames.length)
{
startFrame = 0;
}
if (component.currentAnim !== this)
{
component.currentAnim = this;
2017-04-06 23:34:06 +00:00
component._timeScale = 1;
component.frameRate = this.frameRate;
component.duration = this.duration;
component.msPerFrame = this.msPerFrame;
component.skipMissedFrames = this.skipMissedFrames;
component._delay = this.delay;
component._repeat = this.repeat;
component._repeatDelay = this.repeatDelay;
component._yoyo = this.yoyo;
component._callbackArgs[1] = this;
component._updateParams = component._callbackArgs.concat(this.onUpdateParams);
}
2017-04-05 01:06:28 +00:00
component.updateFrame(this.frames[startFrame]);
},
checkFrame: function (index)
{
return (index < this.frames.length);
},
getFirstTick: function (component, includeDelay)
{
if (includeDelay === undefined) { includeDelay = true; }
// When is the first update due?
component.accumulator = 0;
component.nextTick = component.msPerFrame + component.currentFrame.duration;
if (includeDelay)
{
component.nextTick += (component._delay * 1000);
}
},
2017-04-05 00:15:53 +00:00
getNextTick: function (component)
{
// When is the next update due?
component.accumulator -= component.nextTick;
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-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
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;
component.updateFrame(frame.prevFrame);
// Delay for the current frame
this.getNextTick(component);
2017-04-05 01:06:28 +00:00
}
else if (component.repeatCounter > 0)
{
// Repeat (happens before complete)
this.repeatAnimation(component);
}
else
{
this.completeAnimation(component);
2017-04-05 01:06:28 +00:00
}
}
else
{
component.updateFrame(frame.nextFrame);
this.getNextTick(component);
}
2017-04-05 01:06:28 +00:00
},
previousFrame: function (component)
{
var frame = component.currentFrame;
// TODO: Add frame skip support
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
{
this.completeAnimation(component);
2017-04-05 01:06:28 +00:00
}
}
else
{
component.updateFrame(frame.prevFrame);
this.getNextTick(component);
}
},
repeatAnimation: function (component)
{
if (component._repeatDelay > 0 && component.pendingRepeat === false)
{
component.pendingRepeat = true;
component.accumulator -= component.nextTick;
component.nextTick += (component._repeatDelay * 1000);
}
else
{
component.repeatCounter--;
component.forward = true;
component.updateFrame(component.currentFrame.nextFrame);
this.getNextTick(component);
component.pendingRepeat = false;
if (this.onRepeat)
{
this.onRepeat.apply(this.callbackScope, component._callbackArgs.concat(this.onRepeatParams));
}
}
2017-04-05 01:06:28 +00:00
},
completeAnimation: function (component)
{
if (this.hideOnComplete)
{
component.parent.visible = false;
}
component.stop(true);
},
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
}
},
toJSON: function ()
{
var output = {
key: this.key,
type: 'frame',
frames: [],
framerate: this.frameRate,
duration: this.duration,
skipMissedFrames: this.skipMissedFrames,
delay: this.delay,
repeat: this.repeat,
repeatDelay: this.repeatDelay,
yoyo: this.yoyo,
showOnStart: this.showOnStart,
hideOnComplete: this.hideOnComplete
};
this.frames.forEach(function (frame)
{
output.frames.push(frame.toJSON());
});
return output;
},
destroy: function ()
{
}
};
module.exports = Animation;