UNPKG

phaser

Version:

A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers.

1,388 lines (1,155 loc) 38.7 kB
/** * @author Richard Davey <rich@photonstorm.com> * @copyright 2018 Photon Storm Ltd. * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} */ var Class = require('../../utils/Class'); var GameObjectCreator = require('../../gameobjects/GameObjectCreator'); var GameObjectFactory = require('../../gameobjects/GameObjectFactory'); var TWEEN_CONST = require('./const'); /** * @classdesc * [description] * * @class Tween * @memberOf Phaser.Tweens * @constructor * @since 3.0.0 * * @param {(Phaser.Tweens.TweenManager|Phaser.Tweens.Timeline)} parent - [description] * @param {Phaser.Tweens.TweenDataConfig[]} data - [description] * @param {array} targets - [description] */ var Tween = new Class({ initialize: function Tween (parent, data, targets) { /** * [description] * * @name Phaser.Tweens.Tween#parent * @type {(Phaser.Tweens.TweenManager|Phaser.Tweens.Timeline)} * @since 3.0.0 */ this.parent = parent; /** * Is the parent of this Tween a Timeline? * * @name Phaser.Tweens.Tween#parentIsTimeline * @type {boolean} * @since 3.0.0 */ this.parentIsTimeline = parent.hasOwnProperty('isTimeline'); /** * An array of TweenData objects, each containing a unique property and target being tweened. * * @name Phaser.Tweens.Tween#data * @type {Phaser.Tweens.TweenDataConfig[]} * @since 3.0.0 */ this.data = data; /** * data array doesn't change, so we can cache the length * * @name Phaser.Tweens.Tween#totalData * @type {integer} * @since 3.0.0 */ this.totalData = data.length; /** * An array of references to the target/s this Tween is operating on * * @name Phaser.Tweens.Tween#targets * @type {object[]} * @since 3.0.0 */ this.targets = targets; /** * Cached target total (not necessarily the same as the data total) * * @name Phaser.Tweens.Tween#totalTargets * @type {integer} * @since 3.0.0 */ this.totalTargets = targets.length; /** * If true then duration, delay, etc values are all frame totals. * * @name Phaser.Tweens.Tween#useFrames * @type {boolean} * @default false * @since 3.0.0 */ this.useFrames = 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. * Value isn't used when calculating total duration of the tween, it's a run-time delta adjustment only. * * @name Phaser.Tweens.Tween#timeScale * @type {number} * @default 1 * @since 3.0.0 */ this.timeScale = 1; /** * Loop this tween? Can be -1 for an infinite loop, or an integer. * When enabled it will play through ALL TweenDatas again (use TweenData.repeat to loop a single TD) * * @name Phaser.Tweens.Tween#loop * @type {number} * @default 0 * @since 3.0.0 */ this.loop = 0; /** * Time in ms/frames before the tween loops. * * @name Phaser.Tweens.Tween#loopDelay * @type {number} * @default 0 * @since 3.0.0 */ this.loopDelay = 0; /** * How many loops are left to run? * * @name Phaser.Tweens.Tween#loopCounter * @type {number} * @default 0 * @since 3.0.0 */ this.loopCounter = 0; /** * Time in ms/frames before the 'onComplete' event fires. This never fires if loop = -1 (as it never completes) * * @name Phaser.Tweens.Tween#completeDelay * @type {number} * @default 0 * @since 3.0.0 */ this.completeDelay = 0; /** * Countdown timer (used by timeline offset, loopDelay and completeDelay) * * @name Phaser.Tweens.Tween#countdown * @type {number} * @default 0 * @since 3.0.0 */ this.countdown = 0; /** * Set only if this Tween is part of a Timeline. * * @name Phaser.Tweens.Tween#offset * @type {number} * @default 0 * @since 3.0.0 */ this.offset = 0; /** * Set only if this Tween is part of a Timeline. The calculated offset amount. * * @name Phaser.Tweens.Tween#calculatedOffset * @type {number} * @default 0 * @since 3.0.0 */ this.calculatedOffset = 0; /** * The current state of the tween * * @name Phaser.Tweens.Tween#state * @type {integer} * @since 3.0.0 */ this.state = TWEEN_CONST.PENDING_ADD; /** * The state of the tween when it was paused (used by Resume) * * @name Phaser.Tweens.Tween#_pausedState * @type {integer} * @private * @since 3.0.0 */ this._pausedState = TWEEN_CONST.PENDING_ADD; /** * Does the Tween start off paused? (if so it needs to be started with Tween.play) * * @name Phaser.Tweens.Tween#paused * @type {boolean} * @default false * @since 3.0.0 */ this.paused = false; /** * Elapsed time in ms/frames of this run through the Tween. * * @name Phaser.Tweens.Tween#elapsed * @type {number} * @default 0 * @since 3.0.0 */ this.elapsed = 0; /** * Total elapsed time in ms/frames of the entire Tween, including looping. * * @name Phaser.Tweens.Tween#totalElapsed * @type {number} * @default 0 * @since 3.0.0 */ this.totalElapsed = 0; /** * Time in ms/frames for the whole Tween to play through once, excluding loop amounts and loop delays. * * @name Phaser.Tweens.Tween#duration * @type {number} * @default 0 * @since 3.0.0 */ this.duration = 0; /** * Value between 0 and 1. The amount through the Tween, excluding loops. * * @name Phaser.Tweens.Tween#progress * @type {number} * @default 0 * @since 3.0.0 */ this.progress = 0; /** * Time in ms/frames for the Tween to complete (including looping) * * @name Phaser.Tweens.Tween#totalDuration * @type {number} * @default 0 * @since 3.0.0 */ this.totalDuration = 0; /** * Value between 0 and 1. The amount through the entire Tween, including looping. * * @name Phaser.Tweens.Tween#totalProgress * @type {number} * @default 0 * @since 3.0.0 */ this.totalProgress = 0; /** * An object containing the various Tween callback references. * * @name Phaser.Tweens.Tween#callbacks * @type {object} * @default 0 * @since 3.0.0 */ this.callbacks = { onComplete: null, onLoop: null, onRepeat: null, onStart: null, onUpdate: null, onYoyo: null }; this.callbackScope; }, /** * [description] * * @method Phaser.Tweens.Tween#getValue * @since 3.0.0 * * @return {number} [description] */ getValue: function () { return this.data[0].current; }, /** * [description] * * @method Phaser.Tweens.Tween#setTimeScale * @since 3.0.0 * * @param {number} value - [description] * * @return {Phaser.Tweens.Tween} This Tween object. */ setTimeScale: function (value) { this.timeScale = value; return this; }, /** * [description] * * @method Phaser.Tweens.Tween#getTimeScale * @since 3.0.0 * * @return {number} [description] */ getTimeScale: function () { return this.timeScale; }, /** * [description] * * @method Phaser.Tweens.Tween#isPlaying * @since 3.0.0 * * @return {boolean} [description] */ isPlaying: function () { return (this.state === TWEEN_CONST.ACTIVE); }, /** * [description] * * @method Phaser.Tweens.Tween#isPaused * @since 3.0.0 * * @return {boolean} [description] */ isPaused: function () { return (this.state === TWEEN_CONST.PAUSED); }, /** * [description] * * @method Phaser.Tweens.Tween#hasTarget * @since 3.0.0 * * @param {object} target - [description] * * @return {boolean} [description] */ hasTarget: function (target) { return (this.targets.indexOf(target) !== -1); }, /** * [description] * * @method Phaser.Tweens.Tween#updateTo * @since 3.0.0 * * @param {string} key - [description] * @param {*} value - [description] * @param {boolean} startToCurrent - [description] * * @return {Phaser.Tweens.Tween} This Tween object. */ updateTo: function (key, value, startToCurrent) { for (var i = 0; i < this.totalData; i++) { var tweenData = this.data[i]; if (tweenData.key === key) { tweenData.end = value; if (startToCurrent) { tweenData.start = tweenData.current; } break; } } return this; }, /** * [description] * * @method Phaser.Tweens.Tween#restart * @since 3.0.0 */ restart: function () { if (this.state === TWEEN_CONST.REMOVED) { this.seek(0); this.parent.makeActive(this); } else { this.stop(); this.play(); } }, /** * [description] * * @method Phaser.Tweens.Tween#calcDuration * @since 3.0.0 */ calcDuration: function () { var max = 0; var data = this.data; for (var i = 0; i < this.totalData; i++) { var tweenData = data[i]; // Set t1 (duration + hold + yoyo) tweenData.t1 = tweenData.duration + tweenData.hold; if (tweenData.yoyo) { tweenData.t1 += tweenData.duration; } // Set t2 (repeatDelay + duration + hold + yoyo) tweenData.t2 = tweenData.t1 + tweenData.repeatDelay; // Total Duration tweenData.totalDuration = tweenData.delay + tweenData.t1; if (tweenData.repeat === -1) { tweenData.totalDuration += (tweenData.t2 * 999999999999); } else if (tweenData.repeat > 0) { tweenData.totalDuration += (tweenData.t2 * tweenData.repeat); } if (tweenData.totalDuration > max) { // Get the longest TweenData from the Tween, used to calculate the Tween TD max = tweenData.totalDuration; } } // Excludes loop values this.duration = max; this.loopCounter = (this.loop === -1) ? 999999999999 : this.loop; if (this.loopCounter > 0) { this.totalDuration = this.duration + this.completeDelay + ((this.duration + this.loopDelay) * this.loopCounter); } else { this.totalDuration = this.duration + this.completeDelay; } }, /** * Called by TweenManager.preUpdate as part of its loop to check pending and active tweens. * Should not be called directly. * * @method Phaser.Tweens.Tween#init * @since 3.0.0 * * @return {boolean} Returns `true` if this Tween should be moved from the pending list to the active list by the Tween Manager. */ init: function () { var data = this.data; var totalTargets = this.totalTargets; for (var i = 0; i < this.totalData; i++) { var tweenData = data[i]; var target = tweenData.target; var gen = tweenData.gen; tweenData.delay = gen.delay(i, totalTargets, target); tweenData.duration = gen.duration(i, totalTargets, target); tweenData.hold = gen.hold(i, totalTargets, target); tweenData.repeat = gen.repeat(i, totalTargets, target); tweenData.repeatDelay = gen.repeatDelay(i, totalTargets, target); } this.calcDuration(); this.progress = 0; this.totalProgress = 0; this.elapsed = 0; this.totalElapsed = 0; // You can't have a paused Tween if it's part of a Timeline if (this.paused && !this.parentIsTimeline) { this.state = TWEEN_CONST.PENDING_ADD; this._pausedState = TWEEN_CONST.INIT; return false; } else { this.state = TWEEN_CONST.INIT; return true; } }, /** * [description] * * @method Phaser.Tweens.Tween#nextState * @since 3.0.0 */ nextState: function () { if (this.loopCounter > 0) { this.elapsed = 0; this.progress = 0; this.loopCounter--; var onLoop = this.callbacks.onLoop; if (onLoop) { onLoop.params[1] = this.targets; onLoop.func.apply(onLoop.scope, onLoop.params); } this.resetTweenData(true); if (this.loopDelay > 0) { this.countdown = this.loopDelay; this.state = TWEEN_CONST.LOOP_DELAY; } else { this.state = TWEEN_CONST.ACTIVE; } } else if (this.completeDelay > 0) { this.countdown = this.completeDelay; this.state = TWEEN_CONST.COMPLETE_DELAY; } else { var onComplete = this.callbacks.onComplete; if (onComplete) { onComplete.params[1] = this.targets; onComplete.func.apply(onComplete.scope, onComplete.params); } this.state = TWEEN_CONST.PENDING_REMOVE; } }, /** * [description] * * @method Phaser.Tweens.Tween#pause * @since 3.0.0 * * @return {Phaser.Tweens.Tween} This Tween object. */ pause: function () { if (this.state === TWEEN_CONST.PAUSED) { return; } this.paused = true; this._pausedState = this.state; this.state = TWEEN_CONST.PAUSED; return this; }, /** * [description] * * @method Phaser.Tweens.Tween#play * @since 3.0.0 * * @param {boolean} resetFromTimeline - [description] */ play: function (resetFromTimeline) { if (this.state === TWEEN_CONST.ACTIVE) { return; } else if (this.state === TWEEN_CONST.PENDING_REMOVE || this.state === TWEEN_CONST.REMOVED) { this.init(); this.parent.makeActive(this); resetFromTimeline = true; } var onStart = this.callbacks.onStart; if (this.parentIsTimeline) { this.resetTweenData(resetFromTimeline); if (this.calculatedOffset === 0) { if (onStart) { onStart.params[1] = this.targets; onStart.func.apply(onStart.scope, onStart.params); } this.state = TWEEN_CONST.ACTIVE; } else { this.countdown = this.calculatedOffset; this.state = TWEEN_CONST.OFFSET_DELAY; } } else if (this.paused) { this.paused = false; this.parent.makeActive(this); } else { this.resetTweenData(resetFromTimeline); this.state = TWEEN_CONST.ACTIVE; if (onStart) { onStart.params[1] = this.targets; onStart.func.apply(onStart.scope, onStart.params); } } }, /** * [description] * * @method Phaser.Tweens.Tween#resetTweenData * @since 3.0.0 * * @param {boolean} resetFromLoop - [description] */ resetTweenData: function (resetFromLoop) { var data = this.data; for (var i = 0; i < this.totalData; i++) { var tweenData = data[i]; tweenData.progress = 0; tweenData.elapsed = 0; tweenData.repeatCounter = (tweenData.repeat === -1) ? 999999999999 : tweenData.repeat; if (resetFromLoop) { tweenData.start = tweenData.getStartValue(tweenData.target, tweenData.key, tweenData.start); tweenData.end = tweenData.getEndValue(tweenData.target, tweenData.key, tweenData.end); tweenData.current = tweenData.start; tweenData.state = TWEEN_CONST.PLAYING_FORWARD; } else if (tweenData.delay > 0) { tweenData.elapsed = tweenData.delay; tweenData.state = TWEEN_CONST.DELAY; } else { tweenData.state = TWEEN_CONST.PENDING_RENDER; } } }, /** * [description] * * @method Phaser.Tweens.Tween#resume * @since 3.0.0 * * @return {Phaser.Tweens.Tween} This Tween object. */ resume: function () { if (this.state === TWEEN_CONST.PAUSED) { this.paused = false; this.state = this._pausedState; } else { this.play(); } return this; }, /** * [description] * * @method Phaser.Tweens.Tween#seek * @since 3.0.0 * * @param {float} toPosition - A value between 0 and 1. */ seek: function (toPosition) { var data = this.data; for (var i = 0; i < this.totalData; i++) { // This won't work with loop > 0 yet var ms = this.totalDuration * toPosition; var tweenData = data[i]; var progress = 0; var elapsed = 0; if (ms <= tweenData.delay) { progress = 0; elapsed = 0; } else if (ms >= tweenData.totalDuration) { progress = 1; elapsed = tweenData.duration; } else if (ms > tweenData.delay && ms <= tweenData.t1) { // Keep it zero bound ms = Math.max(0, ms - tweenData.delay); // Somewhere in the first playthru range progress = ms / tweenData.t1; elapsed = tweenData.duration * progress; } else if (ms > tweenData.t1 && ms < tweenData.totalDuration) { // Somewhere in repeat land ms -= tweenData.delay; ms -= tweenData.t1; // var repeats = Math.floor(ms / tweenData.t2); // remainder ms = ((ms / tweenData.t2) % 1) * tweenData.t2; if (ms > tweenData.repeatDelay) { progress = ms / tweenData.t1; elapsed = tweenData.duration * progress; } } tweenData.progress = progress; tweenData.elapsed = elapsed; var v = tweenData.ease(tweenData.progress); tweenData.current = tweenData.start + ((tweenData.end - tweenData.start) * v); // console.log(tweenData.key, 'Seek', tweenData.target[tweenData.key], 'to', tweenData.current, 'pro', tweenData.progress, 'marker', toPosition, progress); // if (tweenData.current === 0) // { // console.log('zero', tweenData.start, tweenData.end, v, 'progress', progress); // } tweenData.target[tweenData.key] = tweenData.current; } }, /** * [description] * * @method Phaser.Tweens.Tween#setCallback * @since 3.0.0 * * @param {string} type - [description] * @param {function} callback - [description] * @param {array} [params] - [description] * @param {object} [scope] - [description] * * @return {Phaser.Tweens.Tween} This Tween object. */ setCallback: function (type, callback, params, scope) { this.callbacks[type] = { func: callback, scope: scope, params: params }; return this; }, /** * 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 have an onComplete callback, then call `Tween.stop` instead. * * @method Phaser.Tweens.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. */ complete: function (delay) { if (delay === undefined) { delay = 0; } if (delay) { this.countdown = delay; this.state = TWEEN_CONST.COMPLETE_DELAY; } else { var onComplete = this.callbacks.onComplete; if (onComplete) { onComplete.params[1] = this.targets; onComplete.func.apply(onComplete.scope, onComplete.params); } this.state = TWEEN_CONST.PENDING_REMOVE; } }, /** * Stops the Tween immediately, whatever stage of progress it is at and flags it for removal by the TweenManager. * * @method Phaser.Tweens.Tween#stop * @since 3.0.0 * * @param {float} [resetTo] - A value between 0 and 1. */ stop: function (resetTo) { if (this.state === TWEEN_CONST.ACTIVE) { if (resetTo !== undefined) { this.seek(resetTo); } } if (this.state !== TWEEN_CONST.REMOVED) { this.state = TWEEN_CONST.PENDING_REMOVE; } }, /** * [description] * * @method Phaser.Tweens.Tween#update * @since 3.0.0 * * @param {number} timestamp - [description] * @param {number} delta - [description] * * @return {boolean} Returns `true` if this Tween has finished and should be removed from the Tween Manager, otherwise returns `false`. */ update: function (timestamp, delta) { if (this.state === TWEEN_CONST.PAUSED) { return false; } if (this.useFrames) { delta = 1 * this.parent.timeScale; } delta *= this.timeScale; this.elapsed += delta; this.progress = Math.min(this.elapsed / this.duration, 1); this.totalElapsed += delta; this.totalProgress = Math.min(this.totalElapsed / this.totalDuration, 1); switch (this.state) { case TWEEN_CONST.ACTIVE: var stillRunning = false; for (var i = 0; i < this.totalData; i++) { if (this.updateTweenData(this, this.data[i], delta)) { stillRunning = true; } } // Anything still running? If not, we're done if (!stillRunning) { this.nextState(); } break; case TWEEN_CONST.LOOP_DELAY: this.countdown -= delta; if (this.countdown <= 0) { this.state = TWEEN_CONST.ACTIVE; } break; case TWEEN_CONST.OFFSET_DELAY: this.countdown -= delta; if (this.countdown <= 0) { var onStart = this.callbacks.onStart; if (onStart) { onStart.params[1] = this.targets; onStart.func.apply(onStart.scope, onStart.params); } this.state = TWEEN_CONST.ACTIVE; } break; case TWEEN_CONST.COMPLETE_DELAY: this.countdown -= delta; if (this.countdown <= 0) { var onComplete = this.callbacks.onComplete; if (onComplete) { onComplete.func.apply(onComplete.scope, onComplete.params); } this.state = TWEEN_CONST.PENDING_REMOVE; } break; } return (this.state === TWEEN_CONST.PENDING_REMOVE); }, /** * [description] * * @method Phaser.Tweens.Tween#setStateFromEnd * @since 3.0.0 * * @param {Phaser.Tweens.Tween} tween - [description] * @param {Phaser.Tweens.TweenDataConfig} tweenData - [description] * @param {number} diff - [description] * * @return {integer} The state of this Tween. */ setStateFromEnd: function (tween, tweenData, diff) { if (tweenData.yoyo) { // We've hit the end of a Playing Forward TweenData and we have a yoyo // Account for any extra time we got from the previous frame tweenData.elapsed = diff; tweenData.progress = diff / tweenData.duration; if (tweenData.flipX) { tweenData.target.toggleFlipX(); } // Problem: The flip and callback and so on gets called for every TweenData that triggers it at the same time. // If you're tweening several properties it can fire for all of them, at once. if (tweenData.flipY) { tweenData.target.toggleFlipY(); } var onYoyo = tween.callbacks.onYoyo; if (onYoyo) { // Element 1 is reserved for the target of the yoyo (and needs setting here) onYoyo.params[1] = tweenData.target; onYoyo.func.apply(onYoyo.scope, onYoyo.params); } tweenData.start = tweenData.getStartValue(tweenData.target, tweenData.key, tweenData.start); return TWEEN_CONST.PLAYING_BACKWARD; } else if (tweenData.repeatCounter > 0) { // We've hit the end of a Playing Forward TweenData and we have a Repeat. // So we're going to go right back to the start to repeat it again. tweenData.repeatCounter--; // Account for any extra time we got from the previous frame tweenData.elapsed = diff; tweenData.progress = diff / tweenData.duration; if (tweenData.flipX) { tweenData.target.toggleFlipX(); } if (tweenData.flipY) { tweenData.target.toggleFlipY(); } var onRepeat = tween.callbacks.onRepeat; if (onRepeat) { // Element 1 is reserved for the target of the repeat (and needs setting here) onRepeat.params[1] = tweenData.target; onRepeat.func.apply(onRepeat.scope, onRepeat.params); } tweenData.start = tweenData.getStartValue(tweenData.target, tweenData.key, tweenData.start); tweenData.end = tweenData.getEndValue(tweenData.target, tweenData.key, tweenData.start); // Delay? if (tweenData.repeatDelay > 0) { tweenData.elapsed = tweenData.repeatDelay - diff; tweenData.current = tweenData.start; tweenData.target[tweenData.key] = tweenData.current; return TWEEN_CONST.REPEAT_DELAY; } else { return TWEEN_CONST.PLAYING_FORWARD; } } return TWEEN_CONST.COMPLETE; }, /** * Was PLAYING_BACKWARD and has hit the start. * * @method Phaser.Tweens.Tween#setStateFromStart * @since 3.0.0 * * @param {Phaser.Tweens.Tween} tween - [description] * @param {Phaser.Tweens.TweenDataConfig} tweenData - [description] * @param {number} diff - [description] * * @return {integer} The state of this Tween. */ setStateFromStart: function (tween, tweenData, diff) { if (tweenData.repeatCounter > 0) { tweenData.repeatCounter--; // Account for any extra time we got from the previous frame tweenData.elapsed = diff; tweenData.progress = diff / tweenData.duration; if (tweenData.flipX) { tweenData.target.toggleFlipX(); } if (tweenData.flipY) { tweenData.target.toggleFlipY(); } var onRepeat = tween.callbacks.onRepeat; if (onRepeat) { // Element 1 is reserved for the target of the repeat (and needs setting here) onRepeat.params[1] = tweenData.target; onRepeat.func.apply(onRepeat.scope, onRepeat.params); } tweenData.end = tweenData.getEndValue(tweenData.target, tweenData.key, tweenData.start); // Delay? if (tweenData.repeatDelay > 0) { tweenData.elapsed = tweenData.repeatDelay - diff; tweenData.current = tweenData.start; tweenData.target[tweenData.key] = tweenData.current; return TWEEN_CONST.REPEAT_DELAY; } else { return TWEEN_CONST.PLAYING_FORWARD; } } return TWEEN_CONST.COMPLETE; }, // /** * [description] * * @method Phaser.Tweens.Tween#updateTweenData * @since 3.0.0 * * @param {Phaser.Tweens.Tween} tween - [description] * @param {Phaser.Tweens.TweenDataConfig} tweenData - [description] * @param {number} delta - Either a value in ms, or 1 if Tween.useFrames is true * * @return {boolean} [description] */ updateTweenData: function (tween, tweenData, delta) { switch (tweenData.state) { case TWEEN_CONST.PLAYING_FORWARD: case TWEEN_CONST.PLAYING_BACKWARD: if (!tweenData.target) { tweenData.state = TWEEN_CONST.COMPLETE; break; } var elapsed = tweenData.elapsed; var duration = tweenData.duration; var diff = 0; elapsed += delta; if (elapsed > duration) { diff = elapsed - duration; elapsed = duration; } var forward = (tweenData.state === TWEEN_CONST.PLAYING_FORWARD); var progress = elapsed / duration; var v; if (forward) { v = tweenData.ease(progress); } else { v = tweenData.ease(1 - progress); } tweenData.current = tweenData.start + ((tweenData.end - tweenData.start) * v); tweenData.target[tweenData.key] = tweenData.current; tweenData.elapsed = elapsed; tweenData.progress = progress; var onUpdate = tween.callbacks.onUpdate; if (onUpdate) { onUpdate.params[1] = tweenData.target; onUpdate.func.apply(onUpdate.scope, onUpdate.params); } if (progress === 1) { if (forward) { if (tweenData.hold > 0) { tweenData.elapsed = tweenData.hold - diff; tweenData.state = TWEEN_CONST.HOLD_DELAY; } else { tweenData.state = this.setStateFromEnd(tween, tweenData, diff); } } else { tweenData.state = this.setStateFromStart(tween, tweenData, diff); } } break; case TWEEN_CONST.DELAY: tweenData.elapsed -= delta; if (tweenData.elapsed <= 0) { tweenData.elapsed = Math.abs(tweenData.elapsed); tweenData.state = TWEEN_CONST.PENDING_RENDER; } break; case TWEEN_CONST.REPEAT_DELAY: tweenData.elapsed -= delta; if (tweenData.elapsed <= 0) { tweenData.elapsed = Math.abs(tweenData.elapsed); tweenData.state = TWEEN_CONST.PLAYING_FORWARD; } break; case TWEEN_CONST.HOLD_DELAY: tweenData.elapsed -= delta; if (tweenData.elapsed <= 0) { tweenData.state = this.setStateFromEnd(tween, tweenData, Math.abs(tweenData.elapsed)); } break; case TWEEN_CONST.PENDING_RENDER: if (tweenData.target) { tweenData.start = tweenData.getStartValue(tweenData.target, tweenData.key, tweenData.target[tweenData.key]); tweenData.end = tweenData.getEndValue(tweenData.target, tweenData.key, tweenData.start); tweenData.current = tweenData.start; tweenData.target[tweenData.key] = tweenData.start; tweenData.state = TWEEN_CONST.PLAYING_FORWARD; } else { tweenData.state = TWEEN_CONST.COMPLETE; } break; } // Return TRUE if this TweenData still playing, otherwise return FALSE return (tweenData.state !== TWEEN_CONST.COMPLETE); } }); Tween.TYPES = [ 'onComplete', 'onLoop', 'onRepeat', 'onStart', 'onUpdate', 'onYoyo' ]; /** * Creates a new Tween object. * * Note: This method will only be available Tweens have been built into Phaser. * * @method Phaser.GameObjects.GameObjectFactory#tween * @since 3.0.0 * * @param {object} config - The Tween configuration. * * @return {Phaser.Tweens.Tween} The Tween that was created. */ GameObjectFactory.register('tween', function (config) { return this.scene.sys.tweens.add(config); }); // When registering a factory function 'this' refers to the GameObjectFactory context. // // There are several properties available to use: // // this.scene - a reference to the Scene that owns the GameObjectFactory // this.displayList - a reference to the Display List the Scene owns // this.updateList - a reference to the Update List the Scene owns /** * Creates a new Tween object and returns it. * * Note: This method will only be available if Tweens have been built into Phaser. * * @method Phaser.GameObjects.GameObjectCreator#tween * @since 3.0.0 * * @param {object} config - The Tween configuration. * * @return {Phaser.Tweens.Tween} The Tween that was created. */ GameObjectCreator.register('tween', function (config) { return this.scene.sys.tweens.create(config); }); // When registering a factory function 'this' refers to the GameObjectCreator context. module.exports = Tween;