mirror of
https://github.com/photonstorm/phaser
synced 2024-12-01 08:59:31 +00:00
572 lines
17 KiB
JavaScript
572 lines
17 KiB
JavaScript
/**
|
|
* @author Richard Davey <rich@photonstorm.com>
|
|
* @copyright 2016 Photon Storm Ltd.
|
|
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
|
|
*/
|
|
|
|
/**
|
|
* A Phaser.Tween contains at least one TweenData object. It contains all of the tween data values, such as the
|
|
* starting and ending values, the ease function, interpolation and duration. The Tween acts as a timeline manager for
|
|
* TweenData objects and can contain multiple TweenData objects.
|
|
*
|
|
* @class Phaser.TweenData
|
|
* @constructor
|
|
* @param {Phaser.Tween} parent - The Tween that owns this TweenData object.
|
|
*/
|
|
Phaser.TweenData = function (parent)
|
|
{
|
|
/**
|
|
* @property {Phaser.Tween} parent - The Tween which owns this TweenData.
|
|
*/
|
|
this.parent = parent;
|
|
|
|
/**
|
|
* @property {Phaser.State} state - A reference to the currently running State.
|
|
*/
|
|
this.state = parent.state;
|
|
|
|
/**
|
|
* @property {object} vStart - An object containing the values at the start of the tween.
|
|
* @private
|
|
*/
|
|
this.vStart = {};
|
|
|
|
/**
|
|
* @property {object} vStartCache - Cached starting values.
|
|
* @private
|
|
*/
|
|
this.vStartCache = {};
|
|
|
|
/**
|
|
* @property {object} vEnd - An object containing the values at the end of the tween.
|
|
* @private
|
|
*/
|
|
this.vEnd = {};
|
|
|
|
/**
|
|
* @property {object} vEndCache - Cached ending values.
|
|
* @private
|
|
*/
|
|
this.vEndCache = {};
|
|
|
|
/**
|
|
* @property {number} duration - The duration of the tween in ms.
|
|
* @default
|
|
*/
|
|
this.duration = 1000;
|
|
|
|
/**
|
|
* @property {number} percent - A value between 0 and 1 that represents how far through the duration this tween is.
|
|
* @readonly
|
|
*/
|
|
this.percent = 0;
|
|
|
|
/**
|
|
* @property {number} value - The current calculated value.
|
|
* @readonly
|
|
*/
|
|
this.value = 0;
|
|
|
|
/**
|
|
* @property {number} repeatCounter - If the Tween is set to repeat this contains the current repeat count.
|
|
*/
|
|
this.repeatCounter = 0;
|
|
|
|
/**
|
|
* @property {number} repeatDelay - The amount of time in ms between repeats of this tween.
|
|
*/
|
|
this.repeatDelay = 0;
|
|
|
|
/**
|
|
* @property {number} repeatTotal - The total number of times this Tween will repeat.
|
|
* @readonly
|
|
*/
|
|
this.repeatTotal = 0;
|
|
|
|
/**
|
|
* @property {boolean} interpolate - True if the Tween will use interpolation (i.e. is an Array to Array tween)
|
|
* @default
|
|
*/
|
|
this.interpolate = false;
|
|
|
|
/**
|
|
* @property {boolean} yoyo - True if the Tween is set to yoyo, otherwise false.
|
|
* @default
|
|
*/
|
|
this.yoyo = false;
|
|
|
|
/**
|
|
* @property {number} yoyoDelay - The amount of time in ms between yoyos of this tween.
|
|
*/
|
|
this.yoyoDelay = 0;
|
|
|
|
/**
|
|
* @property {boolean} inReverse - When a Tween is yoyoing this value holds if it's currently playing forwards (false) or in reverse (true).
|
|
* @default
|
|
*/
|
|
this.inReverse = false;
|
|
|
|
/**
|
|
* @property {number} delay - The amount to delay by until the Tween starts (in ms). Only applies to the start, use repeatDelay to handle repeats.
|
|
* @default
|
|
*/
|
|
this.delay = 0;
|
|
|
|
/**
|
|
* @property {number} dt - Current time value.
|
|
*/
|
|
this.dt = 0;
|
|
|
|
/**
|
|
* @property {number} startTime - The time the Tween started or null if it hasn't yet started.
|
|
*/
|
|
this.startTime = null;
|
|
|
|
/**
|
|
* @property {function} easingFunction - The easing function used for the Tween.
|
|
* @default Phaser.Easing.Default
|
|
*/
|
|
this.easingFunction = Phaser.Easing.Default;
|
|
|
|
/**
|
|
* @property {function} interpolationFunction - The interpolation function used for the Tween.
|
|
* @default Phaser.Math.linearInterpolation
|
|
*/
|
|
this.interpolationFunction = Phaser.Math.linearInterpolation;
|
|
|
|
/**
|
|
* @property {object} interpolationContext - The interpolation function context used for the Tween.
|
|
* @default Phaser.Math
|
|
*/
|
|
this.interpolationContext = Phaser.Math;
|
|
|
|
/**
|
|
* @property {boolean} isRunning - If the tween is running this is set to `true`. Unless Phaser.Tween a TweenData that is waiting for a delay to expire is *not* considered as running.
|
|
* @default
|
|
*/
|
|
this.isRunning = false;
|
|
|
|
/**
|
|
* @property {boolean} isFrom - Is this a from tween or a to tween?
|
|
* @default
|
|
*/
|
|
this.isFrom = false;
|
|
|
|
};
|
|
|
|
/**
|
|
* @constant
|
|
* @type {number}
|
|
*/
|
|
Phaser.TweenData.PENDING = 0;
|
|
|
|
/**
|
|
* @constant
|
|
* @type {number}
|
|
*/
|
|
Phaser.TweenData.RUNNING = 1;
|
|
|
|
/**
|
|
* @constant
|
|
* @type {number}
|
|
*/
|
|
Phaser.TweenData.LOOPED = 2;
|
|
|
|
/**
|
|
* @constant
|
|
* @type {number}
|
|
*/
|
|
Phaser.TweenData.COMPLETE = 3;
|
|
|
|
Phaser.TweenData.prototype.constructor = Phaser.TweenData;
|
|
|
|
Phaser.TweenData.prototype = {
|
|
|
|
/**
|
|
* Sets this tween to be a `to` tween on the properties given. A `to` tween starts at the current value and tweens to the destination value given.
|
|
* For example a Sprite with an `x` coordinate of 100 could be tweened to `x` 200 by giving a properties object of `{ x: 200 }`.
|
|
*
|
|
* @method Phaser.TweenData#to
|
|
* @param {object} properties - The properties you want to tween, such as `Sprite.x` or `Sound.volume`. Given as a JavaScript object.
|
|
* @param {number} [duration=1000] - Duration of this tween in ms.
|
|
* @param {function} [ease=null] - Easing function. If not set it will default to Phaser.Easing.Default, which is Phaser.Easing.Linear.None by default but can be over-ridden at will.
|
|
* @param {number} [delay=0] - Delay before this tween will start, defaults to 0 (no delay). Value given is in ms.
|
|
* @param {number} [repeat=0] - Should the tween automatically restart once complete? If you want it to run forever set as -1. This ignores any chained tweens.
|
|
* @param {boolean} [yoyo=false] - A tween that yoyos will reverse itself and play backwards automatically. A yoyo'd tween doesn't fire the Tween.onComplete event, so listen for Tween.onLoop instead.
|
|
* @return {Phaser.TweenData} This Tween object.
|
|
*/
|
|
to: function (properties, duration, ease, delay, repeat, yoyo)
|
|
{
|
|
this.vEnd = properties;
|
|
this.duration = duration;
|
|
this.easingFunction = ease;
|
|
this.delay = delay;
|
|
this.repeatTotal = repeat;
|
|
this.yoyo = yoyo;
|
|
|
|
this.isFrom = false;
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* Sets this tween to be a `from` tween on the properties given. A `from` tween sets the target to the destination value and tweens to its current value.
|
|
* For example a Sprite with an `x` coordinate of 100 tweened from `x` 500 would be set to `x` 500 and then tweened to `x` 100 by giving a properties object of `{ x: 500 }`.
|
|
*
|
|
* @method Phaser.TweenData#from
|
|
* @param {object} properties - The properties you want to tween, such as `Sprite.x` or `Sound.volume`. Given as a JavaScript object.
|
|
* @param {number} [duration=1000] - Duration of this tween in ms.
|
|
* @param {function} [ease=null] - Easing function. If not set it will default to Phaser.Easing.Default, which is Phaser.Easing.Linear.None by default but can be over-ridden at will.
|
|
* @param {number} [delay=0] - Delay before this tween will start, defaults to 0 (no delay). Value given is in ms.
|
|
* @param {number} [repeat=0] - Should the tween automatically restart once complete? If you want it to run forever set as -1. This ignores any chained tweens.
|
|
* @param {boolean} [yoyo=false] - A tween that yoyos will reverse itself and play backwards automatically. A yoyo'd tween doesn't fire the Tween.onComplete event, so listen for Tween.onLoop instead.
|
|
* @return {Phaser.TweenData} This Tween object.
|
|
*/
|
|
from: function (properties, duration, ease, delay, repeat, yoyo)
|
|
{
|
|
this.vEnd = properties;
|
|
this.duration = duration;
|
|
this.easingFunction = ease;
|
|
this.delay = delay;
|
|
this.repeatTotal = repeat;
|
|
this.yoyo = yoyo;
|
|
|
|
this.isFrom = true;
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* Starts the Tween running.
|
|
*
|
|
* @method Phaser.TweenData#start
|
|
* @return {Phaser.TweenData} This Tween object.
|
|
*/
|
|
start: function ()
|
|
{
|
|
this.startTime = this.state.sys.mainloop.lastFrameTimeMs + this.delay;
|
|
|
|
if (this.parent.reverse)
|
|
{
|
|
this.dt = this.duration;
|
|
}
|
|
else
|
|
{
|
|
this.dt = 0;
|
|
}
|
|
|
|
if (this.delay > 0)
|
|
{
|
|
this.isRunning = false;
|
|
}
|
|
else
|
|
{
|
|
this.isRunning = true;
|
|
}
|
|
|
|
if (this.isFrom)
|
|
{
|
|
// Reverse them all and instant set them
|
|
for (var property in this.vStartCache)
|
|
{
|
|
this.vStart[property] = this.vEndCache[property];
|
|
this.vEnd[property] = this.vStartCache[property];
|
|
this.parent.target[property] = this.vStart[property];
|
|
}
|
|
}
|
|
|
|
this.value = 0;
|
|
this.yoyoCounter = 0;
|
|
this.repeatCounter = this.repeatTotal;
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* Loads the values from the target object into this Tween.
|
|
*
|
|
* @private
|
|
* @method Phaser.TweenData#loadValues
|
|
* @return {Phaser.TweenData} This Tween object.
|
|
*/
|
|
loadValues: function ()
|
|
{
|
|
for (var property in this.parent.properties)
|
|
{
|
|
// Load the property from the parent object
|
|
this.vStart[property] = this.parent.properties[property];
|
|
|
|
// Check if an Array was provided as property value
|
|
if (Array.isArray(this.vEnd[property]))
|
|
{
|
|
if (this.vEnd[property].length === 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (this.percent === 0)
|
|
{
|
|
// Put the start value at the beginning of the array
|
|
// but we only want to do this once, if the Tween hasn't run before
|
|
this.vEnd[property] = [this.vStart[property]].concat(this.vEnd[property]);
|
|
}
|
|
}
|
|
|
|
if (typeof this.vEnd[property] !== 'undefined')
|
|
{
|
|
if (typeof this.vEnd[property] === 'string')
|
|
{
|
|
// Parses relative end values with start as base (e.g.: +10, -3)
|
|
this.vEnd[property] = this.vStart[property] + parseFloat(this.vEnd[property], 10);
|
|
}
|
|
|
|
this.parent.properties[property] = this.vEnd[property];
|
|
}
|
|
else
|
|
{
|
|
// Null tween
|
|
this.vEnd[property] = this.vStart[property];
|
|
}
|
|
|
|
this.vStartCache[property] = this.vStart[property];
|
|
this.vEndCache[property] = this.vEnd[property];
|
|
}
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
/**
|
|
* Updates this Tween. This is called automatically by Phaser.Tween.
|
|
*
|
|
* @protected
|
|
* @method Phaser.TweenData#update
|
|
* @param {number} time - A timestamp passed in by the Tween parent.
|
|
* @return {number} The current status of this Tween. One of the Phaser.TweenData constants: PENDING, RUNNING, LOOPED or COMPLETE.
|
|
*/
|
|
update: function (frameDelta)
|
|
{
|
|
if (!this.isRunning)
|
|
{
|
|
if (this.state.sys.mainloop.lastFrameTimeMs >= this.startTime)
|
|
{
|
|
this.isRunning = true;
|
|
}
|
|
else
|
|
{
|
|
return Phaser.TweenData.PENDING;
|
|
}
|
|
}
|
|
else if (this.state.sys.mainloop.lastFrameTimeMs < this.startTime)
|
|
{
|
|
// Is Running, but is waiting to repeat
|
|
return Phaser.TweenData.RUNNING;
|
|
}
|
|
|
|
// var ms = (this.parent.frameBased) ? this.game.time.physicsElapsedMS : this.game.time.elapsedMS;
|
|
var ms = frameDelta;
|
|
|
|
if (this.parent.reverse)
|
|
{
|
|
this.dt -= ms * this.parent.timeScale;
|
|
this.dt = Math.max(this.dt, 0);
|
|
}
|
|
else
|
|
{
|
|
this.dt += ms * this.parent.timeScale;
|
|
this.dt = Math.min(this.dt, this.duration);
|
|
}
|
|
|
|
this.percent = this.dt / this.duration;
|
|
|
|
this.value = this.easingFunction(this.percent);
|
|
|
|
for (var property in this.vEnd)
|
|
{
|
|
var start = this.vStart[property];
|
|
var end = this.vEnd[property];
|
|
|
|
if (Array.isArray(end))
|
|
{
|
|
this.parent.target[property] = this.interpolationFunction.call(this.interpolationContext, end, this.value);
|
|
}
|
|
else
|
|
{
|
|
this.parent.target[property] = start + ((end - start) * this.value);
|
|
}
|
|
}
|
|
|
|
if ((!this.parent.reverse && this.percent === 1) || (this.parent.reverse && this.percent === 0))
|
|
{
|
|
return this.repeat();
|
|
}
|
|
|
|
return Phaser.TweenData.RUNNING;
|
|
|
|
},
|
|
|
|
/**
|
|
* This will generate an array populated with the tweened object values from start to end.
|
|
* It works by running the tween simulation at the given frame rate based on the values set-up in Tween.to and Tween.from.
|
|
* Just one play through of the tween data is returned, including yoyo if set.
|
|
*
|
|
* @method Phaser.TweenData#generateData
|
|
* @param {number} [frameRate=60] - The speed in frames per second that the data should be generated at. The higher the value, the larger the array it creates.
|
|
* @return {array} An array of tweened values.
|
|
*/
|
|
generateData: function (frameRate)
|
|
{
|
|
if (this.parent.reverse)
|
|
{
|
|
this.dt = this.duration;
|
|
}
|
|
else
|
|
{
|
|
this.dt = 0;
|
|
}
|
|
|
|
var data = [];
|
|
var complete = false;
|
|
var fps = (1 / frameRate) * 1000;
|
|
|
|
do
|
|
{
|
|
if (this.parent.reverse)
|
|
{
|
|
this.dt -= fps;
|
|
this.dt = Math.max(this.dt, 0);
|
|
}
|
|
else
|
|
{
|
|
this.dt += fps;
|
|
this.dt = Math.min(this.dt, this.duration);
|
|
}
|
|
|
|
this.percent = this.dt / this.duration;
|
|
|
|
this.value = this.easingFunction(this.percent);
|
|
|
|
var blob = {};
|
|
|
|
for (var property in this.vEnd)
|
|
{
|
|
var start = this.vStart[property];
|
|
var end = this.vEnd[property];
|
|
|
|
if (Array.isArray(end))
|
|
{
|
|
blob[property] = this.interpolationFunction(end, this.value);
|
|
}
|
|
else
|
|
{
|
|
blob[property] = start + ((end - start) * this.value);
|
|
}
|
|
}
|
|
|
|
data.push(blob);
|
|
|
|
if ((!this.parent.reverse && this.percent === 1) || (this.parent.reverse && this.percent === 0))
|
|
{
|
|
complete = true;
|
|
}
|
|
|
|
} while (!complete);
|
|
|
|
if (this.yoyo)
|
|
{
|
|
var reversed = data.slice();
|
|
reversed.reverse();
|
|
data = data.concat(reversed);
|
|
}
|
|
|
|
return data;
|
|
|
|
},
|
|
|
|
/**
|
|
* Checks if this Tween is meant to repeat or yoyo and handles doing so.
|
|
*
|
|
* @private
|
|
* @method Phaser.TweenData#repeat
|
|
* @return {number} Either Phaser.TweenData.LOOPED or Phaser.TweenData.COMPLETE.
|
|
*/
|
|
repeat: function ()
|
|
{
|
|
var property;
|
|
|
|
// If not a yoyo and repeatCounter = 0 then we're done
|
|
if (this.yoyo)
|
|
{
|
|
// We're already in reverse mode, which means the yoyo has finished and there's no repeats, so end
|
|
if (this.inReverse && this.repeatCounter === 0)
|
|
{
|
|
// Restore the properties
|
|
for (property in this.vStartCache)
|
|
{
|
|
this.vStart[property] = this.vStartCache[property];
|
|
this.vEnd[property] = this.vEndCache[property];
|
|
}
|
|
|
|
this.inReverse = false;
|
|
|
|
return Phaser.TweenData.COMPLETE;
|
|
}
|
|
|
|
this.inReverse = !this.inReverse;
|
|
}
|
|
else if (this.repeatCounter === 0)
|
|
{
|
|
return Phaser.TweenData.COMPLETE;
|
|
}
|
|
|
|
if (this.inReverse)
|
|
{
|
|
// If inReverse we're going from vEnd to vStartCache
|
|
for (property in this.vStartCache)
|
|
{
|
|
this.vStart[property] = this.vEndCache[property];
|
|
this.vEnd[property] = this.vStartCache[property];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If not inReverse we're just repopulating the cache again
|
|
for (property in this.vStartCache)
|
|
{
|
|
this.vStart[property] = this.vStartCache[property];
|
|
this.vEnd[property] = this.vEndCache[property];
|
|
}
|
|
|
|
// -1 means repeat forever, otherwise decrement the repeatCounter
|
|
// We only decrement this counter if the tween isn't doing a yoyo, as that doesn't count towards the repeat total
|
|
if (this.repeatCounter > 0)
|
|
{
|
|
this.repeatCounter--;
|
|
}
|
|
}
|
|
|
|
this.startTime = this.state.sys.mainloop.lastFrameTimeMs;
|
|
|
|
if (this.yoyo && this.inReverse)
|
|
{
|
|
this.startTime += this.yoyoDelay;
|
|
}
|
|
else if (!this.inReverse)
|
|
{
|
|
this.startTime += this.repeatDelay;
|
|
}
|
|
|
|
if (this.parent.reverse)
|
|
{
|
|
this.dt = this.duration;
|
|
}
|
|
else
|
|
{
|
|
this.dt = 0;
|
|
}
|
|
|
|
return Phaser.TweenData.LOOPED;
|
|
|
|
}
|
|
|
|
};
|