UNPKG

@puxi/tween

Version:

PixiJS tweening library

391 lines (383 loc) 11.1 kB
/*! * @puxi/tween - v1.0.1 * Compiled Sun, 26 Jul 2020 02:14:25 UTC * * @puxi/tween is licensed under the MIT License. * http://www.opensource.org/licenses/mit-license */ // cjs 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var PIXI = require('pixi.js'); /** * Holds the information needed to perform a tweening operation. It is used internally * by `PUXI.tween.TweenManager`. * * @memberof PUXI.tween * @class * @template T */ class Tween extends PIXI.utils.EventEmitter { constructor(// eslint-disable-line max-params manager, key, startValue, endValue, erp, ease, observedValue, startTime, endTime, repeat = 1, flip = true) { super(); /** * The tween-manager whose update loop handles this tween. * @member {PUXI.TweenManager} */ this.manager = manager; /** * Unique id for this tweening operation * @member {string} */ this.key = key; /** * Start value of interpolation * @member {T} */ this.startValue = startValue; /** * End value of interpolation * @member {T} */ this.endValue = endValue; /** * Linear interpolator on tween property. * @member {Erp} */ this.erp = erp; /** * Easing function * @member {Ease} */ this.ease = ease; /** * Object that is observed and the interpolated value to be stored in. * @member {T} */ this.observedValue = observedValue; /** * @member {DOMHighResTimeStamp} * @readonly */ this.startTime = startTime; /** * @member {DOMHighResTimeStamp} * @readonly */ this.endTime = endTime; this._repeat = repeat; this._flip = flip; this._next = null; this._target = null; this._observedProperty = null; this.autoCreated = false; } /** * Updates the observed value. * * @param {DOMHighResTimeStamp} t - current time */ update(t = performance.now()) { t = (t - this.startTime) / (this.endTime - this.startTime); t = Math.min(Math.max(t, 0), 1); if (this.ease) { t = this.ease(t); } // Update observed value this.observedValue = this.erp(this.startValue, this.endValue, Math.min(Math.max(t, 0), 1), this.observedValue); // Emit update event this.emit('update', this.observedValue, this.key); // Update target object (if any) if (this._target) { this._target[this._observedProperty] = this.observedValue; } // If cycle completed... if (t >= 1) { --this._repeat; this.emit('cycle', this); // Repeat tween if required if (this._repeat > 0) { if (this._flip) { const { startValue: s, endValue: e } = this; this.endValue = s; this.startValue = e; } const duration = this.endTime - this.startTime; this.startTime += duration; this.endTime += duration; return; } // Initiate chained tween if (this._next) { this.manager.queue(this._next); } this.reset(); // Cleanup after completion this.emit('complete', this); this.removeAllListeners(); } } /** * Configures this tween to update the observed-property on a tween target object * each animation frame. * @template T * @param {PUXI.TweenTarget<T>} target - object on which property is being tweened * @param {string} observedProperty - name of property on target */ target(target, observedProperty) { this._target = target; this._observedProperty = observedProperty; return this; } /** * Repeats this tween `repeat` no. of times again. If the tween is still running, * then this is no. of times it will again (not added to the previous repeat * count). * * Each time the tween is repeated, a `cycle` event is fired. * * By default, the repeat count of any tween is 1. * * @param {number} repeat - the repeat count * @param {boolean}[flip=true] - whether to switch start/end values each cycle * @returns {Tween<T>} - this tween, useful for method chaining */ repeat(repeat, flip = true) { this._repeat = repeat; this._flip = flip; return this; } /** * Chains a tween that will run after this one finishes. * * @template W * @param {W} startValue * @param {W} endValue * @param {DOMHighResTimeStamp} duration * @param {PUXI.Erp<W>} erp * @param {PUXI.Ease}[ease] */ chain(startValue, endValue, duration, erp, ease) { const next = (Tween.pool.pop() || new Tween()); next.manager = this.manager; next.key = 0; next.startValue = startValue; next.endValue = endValue; next.startTime = this.endTime; next.endTime = next.startTime + duration; next.erp = erp; next.ease = ease; this._next = next; return next; } /** * Clears the tween's extra properties. */ reset() { this.ease = null; this._repeat = 0; this._next = null; this._target = null; this._observedProperty = null; } /** * Called when a tween is complete and no references to it are held. This * will pool it (if auto-created). * * Custom tweens should override this. */ destroy() { this.reset(); if (this.autoCreated) { Tween.pool.push(this); } } } /** * Fired whenever the observed value is updated. * @event update * @param {T} observedValue * @param {number} key */ /** * Fired whenever the tween has "repeated" once. * @event cycle * @param {Tween} cxt */ /** * Fired when tween has finished. References to this tween should be removed. * @event complete * @param {Tween} cxt */ /** * Used for pooling. * @member {Array<TweenContext>} * @static */ Tween.pool = []; // TODO: Prevent update loop from starting if there are no queued tweens. exports.nextTweenKey = 0; /** * @memberof PUXI.tween * @class */ class TweenManager { constructor(autoStart = true) { this.onUpdate = () => { for (const [, cxt] of this.tweenMap) { cxt.update(); } }; this.onTweenComplete = (cxt) => { this.tweenMap.delete(cxt.key); cxt.destroy(); }; this.tweenMap = new Map(); if (autoStart) { this.start(); } } /** * Initiates a tween from `startValue` to `endValue` for the given duration * using an interpolator. * * @template {T} * @param {T} startValue - value of tween property at start * @param {T} endValue - value of tween property at finish * @param {DOMHighResTimeStamp | number} duration - duration of tween in milliseconds * @param {PUXI.Erp<T>} erp - interpolator on tween property * @param {PUXI.Ease}[ease] - easing function */ tween(startValue, endValue, duration, erp, ease) { const tweenCxt = (Tween.pool.pop() || new Tween()); tweenCxt.autoCreated = true; tweenCxt.reset(); tweenCxt.manager = this; tweenCxt.key = exports.nextTweenKey++; tweenCxt.startValue = startValue; tweenCxt.endValue = endValue; tweenCxt.erp = erp; tweenCxt.ease = ease; tweenCxt.startTime = performance.now(); tweenCxt.endTime = tweenCxt.startTime + duration; this.tweenMap.set(tweenCxt.key, tweenCxt); tweenCxt.on('complete', this.onTweenComplete); return tweenCxt; } /** * Queues the tween context so that it is updated every frame. * * @param {PUXI.Tween} context * @returns {PUXI.TweenManager} this manager, useful for method chaining */ queue(context) { context.key = exports.nextTweenKey++; this.tweenMap.set(context.key, context); context.on('complete', this.onTweenComplete); return this; } /** * Starts the update loop. */ start() { if (this.isRunning) { return; } PIXI.Ticker.shared.add(this.onUpdate); this.isRunning = true; } /** * Stops the update loop. This will prevent tweens from getting updated. */ stop() { if (!this.isRunning) { return; } PIXI.Ticker.shared.remove(this.onUpdate); this.isRunning = false; } } /** * @memberof PUXI * @typedef {Function} Ease * @param {number} t - interpolation parameter (b/w 0 and 1) that increases linearly * @returns {numeber} - output interpolation parameter (b/w 0 and 1) */ /** * Quadratic ease-in * * @memberof PUXI * @type Ease * @param {number} t * @returns {number} */ const EaseIn = (t) => t * t; /** * Quadratic ease-out * * @memberof PUXI * @type Ease * @param {number} t * @returns {number} */ const EaseOut = (t) => (1 - t) * (1 - t); /** * Quadratic ease-in & ease-out mixed! * * @memberof PUXI * @type Ease * @param {number} t * @returns {number} */ const EaseBoth = (t) => ((t <= 0.5) ? 2 * t * t : ((2 * ((t - 0.5) * (1.5 - t))) + 0.5)); /** * Defines a (linear) interpolator on a type `T`. * * @memberof PUXI * @typedef {Function} Erp * @template T * @param {T} startValue * @param {T} endValue * @param {number} t - interpolation parameter between 0 and 1 * @param {T}[observedValue] */ /** * Interpolation function for number properties like alpha, rotation, component * position/scale/skew, elevation, etc. * * @memberof PUXI * @extends PUXI.Erp<number> * @param {number} startValue * @param {number} endValue * @param {number} t */ const NumberErp = (startValue, endValue, t) => ((1 - t) * startValue) + (t * endValue); /** * Interpolation function for 2D vector properties like position, scale, skew, etc. * * @memberof PUXI * @extends PUXI.Erp<PIXI.Point> * @param {PIXI.Point} startValue * @param {PIXI.Point} endValue * @param {number} t * @param {PIXI.Point} observedValue */ const PointErp = (startValue, endValue, t, observedValue) => { if (!observedValue) { observedValue = new PIXI.Point(); } observedValue.x = ((1 - t) * startValue.x) + (t * endValue.x); observedValue.y = ((1 - t) * startValue.y) + (t * endValue.y); return observedValue; }; exports.EaseBoth = EaseBoth; exports.EaseIn = EaseIn; exports.EaseOut = EaseOut; exports.NumberErp = NumberErp; exports.PointErp = PointErp; exports.Tween = Tween; exports.TweenManager = TweenManager; //# sourceMappingURL=puxi-tween.js.map