phaser
Version:
A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.
894 lines (803 loc) • 27.9 kB
JavaScript
/**
* @author Richard Davey <rich@phaser.io>
* @copyright 2013-2025 Phaser Studio Inc.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var Class = require('../../utils/Class');
var EventEmitter = require('eventemitter3');
var Events = require('../events');
var TWEEN_CONST = require('./const');
/**
* @classdesc
* As the name implies, this is the base Tween class that both the Tween and TweenChain
* inherit from. It contains shared properties and methods common to both types of Tween.
*
* Typically you would never instantiate this class directly, although you could certainly
* use it to create your own variation of Tweens from.
*
* @class BaseTween
* @memberof Phaser.Tweens
* @extends Phaser.Events.EventEmitter
* @constructor
* @since 3.60.0
*
* @param {(Phaser.Tweens.TweenManager|Phaser.Tweens.TweenChain)} parent - A reference to the Tween Manager, or Tween Chain, that owns this Tween.
*/
var BaseTween = new Class({
Extends: EventEmitter,
initialize:
function BaseTween (parent)
{
EventEmitter.call(this);
/**
* A reference to the Tween Manager, or Tween Chain, that owns this Tween.
*
* @name Phaser.Tweens.BaseTween#parent
* @type {(Phaser.Tweens.TweenManager|Phaser.Tweens.TweenChain)}
* @since 3.60.0
*/
this.parent = parent;
/**
* The main data array. For a Tween, this contains all of the `TweenData` objects, each
* containing a unique property and target that is being tweened.
*
* For a TweenChain, this contains an array of `Tween` instances, which are being played
* through in sequence.
*
* @name Phaser.Tweens.BaseTween#data
* @type {(Phaser.Tweens.TweenData[]|Phaser.Tweens.Tween[])}
* @since 3.60.0
*/
this.data = [];
/**
* The cached size of the data array.
*
* @name Phaser.Tweens.BaseTween#totalData
* @type {number}
* @since 3.60.0
*/
this.totalData = 0;
/**
* The time in milliseconds before the 'onStart' event fires.
*
* For a Tween, this is the shortest `delay` value across all of the TweenDatas it owns.
* For a TweenChain, it is whatever delay value was given in the configuration.
*
* @name Phaser.Tweens.BaseTween#startDelay
* @type {number}
* @default 0
* @since 3.60.0
*/
this.startDelay = 0;
/**
* Has this Tween started playback yet?
*
* This boolean is toggled when the Tween leaves the 'start delayed' state and begins running.
*
* @name Phaser.Tweens.BaseTween#hasStarted
* @type {boolean}
* @readonly
* @since 3.60.0
*/
this.hasStarted = false;
/**
* Scales the time applied to this Tween. A value of 1 runs in real-time. A value of 0.5 runs 50% slower, and so on.
*
* The value isn't used when calculating total duration of the tween, it's a run-time delta adjustment only.
*
* This value is multiplied by the `TweenManager.timeScale`.
*
* @name Phaser.Tweens.BaseTween#timeScale
* @type {number}
* @default 1
* @since 3.60.0
*/
this.timeScale = 1;
/**
* The number of times this Tween will loop.
*
* Can be -1 for an infinite loop, zero for none, or a positive integer.
*
* Typically this is set in the configuration object, but can also be set directly
* as long as this Tween is paused and hasn't started playback.
*
* When enabled it will play through ALL Tweens again.
*
* Use TweenData.repeat to loop a single element.
*
* @name Phaser.Tweens.BaseTween#loop
* @type {number}
* @default 0
* @since 3.60.0
*/
this.loop = 0;
/**
* The time in milliseconds before the Tween loops.
*
* Only used if `loop` is > 0.
*
* @name Phaser.Tweens.BaseTween#loopDelay
* @type {number}
* @default 0
* @since 3.60.0
*/
this.loopDelay = 0;
/**
* Internal counter recording how many loops are left to run.
*
* @name Phaser.Tweens.BaseTween#loopCounter
* @type {number}
* @default 0
* @since 3.60.0
*/
this.loopCounter = 0;
/**
* The time in milliseconds before the 'onComplete' event fires.
*
* This never fires if `loop = -1` as it never completes because it has been
* set to loop forever.
*
* @name Phaser.Tweens.BaseTween#completeDelay
* @type {number}
* @default 0
* @since 3.60.0
*/
this.completeDelay = 0;
/**
* An internal countdown timer (used by loopDelay and completeDelay)
*
* @name Phaser.Tweens.BaseTween#countdown
* @type {number}
* @default 0
* @since 3.60.0
*/
this.countdown = 0;
/**
* The current state of the Tween.
*
* @name Phaser.Tweens.BaseTween#state
* @type {Phaser.Tweens.StateType}
* @since 3.60.0
*/
this.state = TWEEN_CONST.PENDING;
/**
* Is the Tween currently paused?
*
* A paused Tween needs to be started with the `play` method, or resumed with the `resume` method.
*
* This property can be toggled at runtime if required.
*
* @name Phaser.Tweens.BaseTween#paused
* @type {boolean}
* @default false
* @since 3.60.0
*/
this.paused = false;
/**
* An object containing the different Tween callback functions.
*
* You can either set these in the Tween config, or by calling the `Tween.setCallback` method.
*
* The types available are:
*
* `onActive` - When the Tween is first created it moves to an 'active' state when added to the Tween Manager. 'Active' does not mean 'playing'.
* `onStart` - When the Tween starts playing after a delayed or paused state. This will happen at the same time as `onActive` if the tween has no delay and isn't paused.
* `onLoop` - When a Tween loops, if it has been set to do so. This happens _after_ the `loopDelay` expires, if set.
* `onComplete` - When the Tween finishes playback fully. Never invoked if the Tween is set to repeat infinitely.
* `onStop` - Invoked only if the `Tween.stop` method is called.
* `onPause` - Invoked only if the `Tween.pause` method is called. Not invoked if the Tween Manager is paused.
* `onResume` - Invoked only if the `Tween.resume` method is called. Not invoked if the Tween Manager is resumed.
*
* The following types are also available and are invoked on a `TweenData` level - that is per-object, per-property, being tweened.
*
* `onYoyo` - When a TweenData starts a yoyo. This happens _after_ the `hold` delay expires, if set.
* `onRepeat` - When a TweenData repeats playback. This happens _after_ the `repeatDelay` expires, if set.
* `onUpdate` - When a TweenData updates a property on a source target during playback.
*
* @name Phaser.Tweens.BaseTween#callbacks
* @type {Phaser.Types.Tweens.TweenCallbacks}
* @since 3.60.0
*/
this.callbacks = {
onActive: null,
onComplete: null,
onLoop: null,
onPause: null,
onRepeat: null,
onResume: null,
onStart: null,
onStop: null,
onUpdate: null,
onYoyo: null
};
/**
* The scope (or context) in which all of the callbacks are invoked.
*
* This defaults to be this Tween, but you can override this property
* to set it to whatever object you require.
*
* @name Phaser.Tweens.BaseTween#callbackScope
* @type {any}
* @since 3.60.0
*/
this.callbackScope;
/**
* Will this Tween persist after playback? A Tween that persists will _not_ be destroyed by the
* Tween Manager, or when calling `Tween.stop`, and can be re-played as required. You can either
* set this property when creating the tween in the tween config, or set it _prior_ to playback.
*
* However, it's up to you to ensure you destroy persistent tweens when you are finished with them,
* or they will retain references you may no longer require and waste memory.
*
* By default, `Tweens` are set to _not_ persist, so they are automatically cleaned-up by
* the Tween Manager.
*
* @name Phaser.Tweens.BaseTween#persist
* @type {boolean}
* @since 3.60.0
*/
this.persist = false;
},
/**
* Sets the value of the time scale applied to this Tween. A value of 1 runs in real-time.
* A value of 0.5 runs 50% slower, and so on.
*
* The value isn't used when calculating total duration of the tween, it's a run-time delta adjustment only.
*
* This value is multiplied by the `TweenManager.timeScale`.
*
* @method Phaser.Tweens.BaseTween#setTimeScale
* @since 3.60.0
*
* @param {number} value - The time scale value to set.
*
* @return {this} This Tween instance.
*/
setTimeScale: function (value)
{
this.timeScale = value;
return this;
},
/**
* Gets the value of the time scale applied to this Tween. A value of 1 runs in real-time.
* A value of 0.5 runs 50% slower, and so on.
*
* @method Phaser.Tweens.BaseTween#getTimeScale
* @since 3.60.0
*
* @return {number} The value of the time scale applied to this Tween.
*/
getTimeScale: function ()
{
return this.timeScale;
},
/**
* Checks if this Tween is currently playing.
*
* If this Tween is paused, or not active, this method will return false.
*
* @method Phaser.Tweens.BaseTween#isPlaying
* @since 3.60.0
*
* @return {boolean} `true` if the Tween is playing, otherwise `false`.
*/
isPlaying: function ()
{
return (!this.paused && this.isActive());
},
/**
* Checks if the Tween is currently paused.
*
* This is the same as inspecting the `BaseTween.paused` property directly.
*
* @method Phaser.Tweens.BaseTween#isPaused
* @since 3.60.0
*
* @return {boolean} `true` if the Tween is paused, otherwise `false`.
*/
isPaused: function ()
{
return this.paused;
},
/**
* Pauses the Tween immediately. Use `resume` to continue playback.
*
* You can also toggle the `Tween.paused` boolean property, but doing so will not trigger the PAUSE event.
*
* @method Phaser.Tweens.BaseTween#pause
* @fires Phaser.Tweens.Events#TWEEN_PAUSE
* @since 3.60.0
*
* @return {this} This Tween instance.
*/
pause: function ()
{
if (!this.paused)
{
this.paused = true;
this.dispatchEvent(Events.TWEEN_PAUSE, 'onPause');
}
return this;
},
/**
* Resumes the playback of a previously paused Tween.
*
* You can also toggle the `Tween.paused` boolean property, but doing so will not trigger the RESUME event.
*
* @method Phaser.Tweens.BaseTween#resume
* @fires Phaser.Tweens.Events#TWEEN_RESUME
* @since 3.60.0
*
* @return {this} This Tween instance.
*/
resume: function ()
{
if (this.paused)
{
this.paused = false;
this.dispatchEvent(Events.TWEEN_RESUME, 'onResume');
}
return this;
},
/**
* Internal method that makes this Tween active within the TweenManager
* and emits the onActive event and callback.
*
* @method Phaser.Tweens.BaseTween#makeActive
* @fires Phaser.Tweens.Events#TWEEN_ACTIVE
* @since 3.60.0
*/
makeActive: function ()
{
this.parent.makeActive(this);
this.dispatchEvent(Events.TWEEN_ACTIVE, 'onActive');
},
/**
* Internal method that handles this tween completing and emitting the onComplete event
* and callback.
*
* @method Phaser.Tweens.BaseTween#onCompleteHandler
* @since 3.60.0
*/
onCompleteHandler: function ()
{
this.setPendingRemoveState();
this.dispatchEvent(Events.TWEEN_COMPLETE, 'onComplete');
},
/**
* Flags the Tween as being complete, whatever stage of progress it is at.
*
* If an `onComplete` callback has been defined it will automatically invoke it, unless a `delay`
* argument is provided, in which case the Tween will delay for that period of time before calling the callback.
*
* If you don't need a delay or don't have an `onComplete` callback then call `Tween.stop` instead.
*
* @method Phaser.Tweens.BaseTween#complete
* @fires Phaser.Tweens.Events#TWEEN_COMPLETE
* @since 3.2.0
*
* @param {number} [delay=0] - The time to wait before invoking the complete callback. If zero it will fire immediately.
*
* @return {this} This Tween instance.
*/
complete: function (delay)
{
if (delay === undefined) { delay = 0; }
if (delay)
{
this.setCompleteDelayState();
this.countdown = delay;
}
else
{
this.onCompleteHandler();
}
return this;
},
/**
* Flags the Tween as being complete only once the current loop has finished.
*
* This is a useful way to stop an infinitely looping tween once a complete cycle is over,
* rather than abruptly.
*
* If you don't have a loop then call `Tween.stop` instead.
*
* @method Phaser.Tweens.BaseTween#completeAfterLoop
* @fires Phaser.Tweens.Events#TWEEN_COMPLETE
* @since 3.60.0
*
* @param {number} [loops=0] - The number of loops that should finish before this tween completes. Zero means complete just the current loop.
*
* @return {this} This Tween instance.
*/
completeAfterLoop: function (loops)
{
if (loops === undefined) { loops = 0; }
if (this.loopCounter > loops)
{
this.loopCounter = loops;
}
return this;
},
/**
* Immediately removes this Tween from the TweenManager and all of its internal arrays,
* no matter what stage it is at. Then sets the tween state to `REMOVED`.
*
* You should dispose of your reference to this tween after calling this method, to
* free it from memory. If you no longer require it, call `Tween.destroy()` on it.
*
* @method Phaser.Tweens.BaseTween#remove
* @since 3.60.0
*
* @return {this} This Tween instance.
*/
remove: function ()
{
if (this.parent)
{
this.parent.remove(this);
}
return this;
},
/**
* Stops the Tween immediately, whatever stage of progress it is at.
*
* If not a part of a Tween Chain it is also flagged for removal by the Tween Manager.
*
* If an `onStop` callback has been defined it will automatically invoke it.
*
* The Tween will be removed during the next game frame, but should be considered 'destroyed' from this point on.
*
* Typically, you cannot play a Tween that has been stopped. If you just wish to pause the tween, not destroy it,
* then call the `pause` method instead and use `resume` to continue playback. If you wish to restart the Tween,
* use the `restart` or `seek` methods.
*
* @method Phaser.Tweens.BaseTween#stop
* @fires Phaser.Tweens.Events#TWEEN_STOP
* @since 3.60.0
*
* @return {this} This Tween instance.
*/
stop: function ()
{
if (this.parent && !this.isRemoved() && !this.isPendingRemove() && !this.isDestroyed())
{
this.dispatchEvent(Events.TWEEN_STOP, 'onStop');
this.setPendingRemoveState();
}
return this;
},
/**
* Internal method that handles the processing of the loop delay countdown timer and
* the dispatch of related events. Called automatically by `Tween.update`.
*
* @method Phaser.Tweens.BaseTween#updateLoopCountdown
* @since 3.60.0
*
* @param {number} delta - The delta time in ms since the last frame. This is a smoothed and capped value based on the FPS rate.
*/
updateLoopCountdown: function (delta)
{
this.countdown -= delta;
if (this.countdown <= 0)
{
this.setActiveState();
this.dispatchEvent(Events.TWEEN_LOOP, 'onLoop');
}
},
/**
* Internal method that handles the processing of the start delay countdown timer and
* the dispatch of related events. Called automatically by `Tween.update`.
*
* @method Phaser.Tweens.BaseTween#updateStartCountdown
* @since 3.60.0
*
* @param {number} delta - The delta time in ms since the last frame. This is a smoothed and capped value based on the FPS rate.
*/
updateStartCountdown: function (delta)
{
this.countdown -= delta;
if (this.countdown <= 0)
{
this.hasStarted = true;
this.setActiveState();
this.dispatchEvent(Events.TWEEN_START, 'onStart');
// Reset the delta so we always start progress from zero
delta = 0;
}
return delta;
},
/**
* Internal method that handles the processing of the complete delay countdown timer and
* the dispatch of related events. Called automatically by `Tween.update`.
*
* @method Phaser.Tweens.BaseTween#updateCompleteDelay
* @since 3.60.0
*
* @param {number} delta - The delta time in ms since the last frame. This is a smoothed and capped value based on the FPS rate.
*/
updateCompleteDelay: function (delta)
{
this.countdown -= delta;
if (this.countdown <= 0)
{
this.onCompleteHandler();
}
},
/**
* Sets an event based callback to be invoked during playback.
*
* Calling this method will replace a previously set callback for the given type, if any exists.
*
* The types available are:
*
* `onActive` - When the Tween is first created it moves to an 'active' state when added to the Tween Manager. 'Active' does not mean 'playing'.
* `onStart` - When the Tween starts playing after a delayed or paused state. This will happen at the same time as `onActive` if the tween has no delay and isn't paused.
* `onLoop` - When a Tween loops, if it has been set to do so. This happens _after_ the `loopDelay` expires, if set.
* `onComplete` - When the Tween finishes playback fully. Never invoked if the Tween is set to repeat infinitely.
* `onStop` - Invoked only if the `Tween.stop` method is called.
* `onPause` - Invoked only if the `Tween.pause` method is called. Not invoked if the Tween Manager is paused.
* `onResume` - Invoked only if the `Tween.resume` method is called. Not invoked if the Tween Manager is resumed.
*
* The following types are also available and are invoked on a `TweenData` level - that is per-object, per-property, being tweened.
*
* `onYoyo` - When a TweenData starts a yoyo. This happens _after_ the `hold` delay expires, if set.
* `onRepeat` - When a TweenData repeats playback. This happens _after_ the `repeatDelay` expires, if set.
* `onUpdate` - When a TweenData updates a property on a source target during playback.
*
* @method Phaser.Tweens.BaseTween#setCallback
* @since 3.60.0
*
* @param {Phaser.Types.Tweens.TweenCallbackTypes} type - The type of callback to set. One of: `onActive`, `onComplete`, `onLoop`, `onPause`, `onRepeat`, `onResume`, `onStart`, `onStop`, `onUpdate` or `onYoyo`.
* @param {function} callback - Your callback that will be invoked.
* @param {array} [params] - The parameters to pass to the callback. Pass an empty array if you don't want to define any, but do wish to set the scope.
*
* @return {this} This Tween instance.
*/
setCallback: function (type, callback, params)
{
if (params === undefined) { params = []; }
if (this.callbacks.hasOwnProperty(type))
{
this.callbacks[type] = { func: callback, params: params };
}
return this;
},
/**
* Sets this Tween state to PENDING.
*
* @method Phaser.Tweens.BaseTween#setPendingState
* @since 3.60.0
*/
setPendingState: function ()
{
this.state = TWEEN_CONST.PENDING;
},
/**
* Sets this Tween state to ACTIVE.
*
* @method Phaser.Tweens.BaseTween#setActiveState
* @since 3.60.0
*/
setActiveState: function ()
{
this.state = TWEEN_CONST.ACTIVE;
this.hasStarted = false;
},
/**
* Sets this Tween state to LOOP_DELAY.
*
* @method Phaser.Tweens.BaseTween#setLoopDelayState
* @since 3.60.0
*/
setLoopDelayState: function ()
{
this.state = TWEEN_CONST.LOOP_DELAY;
},
/**
* Sets this Tween state to COMPLETE_DELAY.
*
* @method Phaser.Tweens.BaseTween#setCompleteDelayState
* @since 3.60.0
*/
setCompleteDelayState: function ()
{
this.state = TWEEN_CONST.COMPLETE_DELAY;
},
/**
* Sets this Tween state to START_DELAY.
*
* @method Phaser.Tweens.BaseTween#setStartDelayState
* @since 3.60.0
*/
setStartDelayState: function ()
{
this.state = TWEEN_CONST.START_DELAY;
this.countdown = this.startDelay;
this.hasStarted = false;
},
/**
* Sets this Tween state to PENDING_REMOVE.
*
* @method Phaser.Tweens.BaseTween#setPendingRemoveState
* @since 3.60.0
*/
setPendingRemoveState: function ()
{
this.state = TWEEN_CONST.PENDING_REMOVE;
},
/**
* Sets this Tween state to REMOVED.
*
* @method Phaser.Tweens.BaseTween#setRemovedState
* @since 3.60.0
*/
setRemovedState: function ()
{
this.state = TWEEN_CONST.REMOVED;
},
/**
* Sets this Tween state to FINISHED.
*
* @method Phaser.Tweens.BaseTween#setFinishedState
* @since 3.60.0
*/
setFinishedState: function ()
{
this.state = TWEEN_CONST.FINISHED;
},
/**
* Sets this Tween state to DESTROYED.
*
* @method Phaser.Tweens.BaseTween#setDestroyedState
* @since 3.60.0
*/
setDestroyedState: function ()
{
this.state = TWEEN_CONST.DESTROYED;
},
/**
* Returns `true` if this Tween has a _current_ state of PENDING, otherwise `false`.
*
* @method Phaser.Tweens.BaseTween#isPending
* @since 3.60.0
*
* @return {boolean} `true` if this Tween has a _current_ state of PENDING, otherwise `false`.
*/
isPending: function ()
{
return (this.state === TWEEN_CONST.PENDING);
},
/**
* Returns `true` if this Tween has a _current_ state of ACTIVE, otherwise `false`.
*
* @method Phaser.Tweens.BaseTween#isActive
* @since 3.60.0
*
* @return {boolean} `true` if this Tween has a _current_ state of ACTIVE, otherwise `false`.
*/
isActive: function ()
{
return (this.state === TWEEN_CONST.ACTIVE);
},
/**
* Returns `true` if this Tween has a _current_ state of LOOP_DELAY, otherwise `false`.
*
* @method Phaser.Tweens.BaseTween#isLoopDelayed
* @since 3.60.0
*
* @return {boolean} `true` if this Tween has a _current_ state of LOOP_DELAY, otherwise `false`.
*/
isLoopDelayed: function ()
{
return (this.state === TWEEN_CONST.LOOP_DELAY);
},
/**
* Returns `true` if this Tween has a _current_ state of COMPLETE_DELAY, otherwise `false`.
*
* @method Phaser.Tweens.BaseTween#isCompleteDelayed
* @since 3.60.0
*
* @return {boolean} `true` if this Tween has a _current_ state of COMPLETE_DELAY, otherwise `false`.
*/
isCompleteDelayed: function ()
{
return (this.state === TWEEN_CONST.COMPLETE_DELAY);
},
/**
* Returns `true` if this Tween has a _current_ state of START_DELAY, otherwise `false`.
*
* @method Phaser.Tweens.BaseTween#isStartDelayed
* @since 3.60.0
*
* @return {boolean} `true` if this Tween has a _current_ state of START_DELAY, otherwise `false`.
*/
isStartDelayed: function ()
{
return (this.state === TWEEN_CONST.START_DELAY);
},
/**
* Returns `true` if this Tween has a _current_ state of PENDING_REMOVE, otherwise `false`.
*
* @method Phaser.Tweens.BaseTween#isPendingRemove
* @since 3.60.0
*
* @return {boolean} `true` if this Tween has a _current_ state of PENDING_REMOVE, otherwise `false`.
*/
isPendingRemove: function ()
{
return (this.state === TWEEN_CONST.PENDING_REMOVE);
},
/**
* Returns `true` if this Tween has a _current_ state of REMOVED, otherwise `false`.
*
* @method Phaser.Tweens.BaseTween#isRemoved
* @since 3.60.0
*
* @return {boolean} `true` if this Tween has a _current_ state of REMOVED, otherwise `false`.
*/
isRemoved: function ()
{
return (this.state === TWEEN_CONST.REMOVED);
},
/**
* Returns `true` if this Tween has a _current_ state of FINISHED, otherwise `false`.
*
* @method Phaser.Tweens.BaseTween#isFinished
* @since 3.60.0
*
* @return {boolean} `true` if this Tween has a _current_ state of FINISHED, otherwise `false`.
*/
isFinished: function ()
{
return (this.state === TWEEN_CONST.FINISHED);
},
/**
* Returns `true` if this Tween has a _current_ state of DESTROYED, otherwise `false`.
*
* @method Phaser.Tweens.BaseTween#isDestroyed
* @since 3.60.0
*
* @return {boolean} `true` if this Tween has a _current_ state of DESTROYED, otherwise `false`.
*/
isDestroyed: function ()
{
return (this.state === TWEEN_CONST.DESTROYED);
},
/**
* Handles the destroy process of this Tween, clearing out the
* Tween Data and resetting the targets. A Tween that has been
* destroyed cannot ever be played or used again.
*
* @method Phaser.Tweens.BaseTween#destroy
* @since 3.60.0
*/
destroy: function ()
{
if (this.data)
{
this.data.forEach(function (tweenData)
{
tweenData.destroy();
});
}
this.removeAllListeners();
this.callbacks = null;
this.data = null;
this.parent = null;
this.setDestroyedState();
}
});
BaseTween.TYPES = [
'onActive',
'onComplete',
'onLoop',
'onPause',
'onRepeat',
'onResume',
'onStart',
'onStop',
'onUpdate',
'onYoyo'
];
module.exports = BaseTween;