/*global define*/ define([ '../Core/clone', '../Core/defaultValue', '../Core/defined', '../Core/defineProperties', '../Core/DeveloperError', '../Core/EasingFunction', '../Core/getTimestamp', '../Core/TimeConstants', '../ThirdParty/Tween' ], function( clone, defaultValue, defined, defineProperties, DeveloperError, EasingFunction, getTimestamp, TimeConstants, TweenJS) { "use strict"; /** * A tween is an animation that interpolates the properties of two objects using an {@link EasingFunction}. Create * one using {@link Scene#tweens} and {@link TweenCollection#add} and related add functions. * * @alias Tween * @constructor * * @private */ var Tween = function(tweens, tweenjs, startObject, stopObject, duration, delay, easingFunction, update, complete, cancel) { this._tweens = tweens; this._tweenjs = tweenjs; this._startObject = clone(startObject); this._stopObject = clone(stopObject); this._duration = duration; this._delay = delay; this._easingFunction = easingFunction; this._update = update; this._complete = complete; /** * The callback to call if the tween is canceled either because {@link Tween#cancelTween} * was called or because the tween was removed from the collection. * * @type {TweenCollection~TweenCancelledCallback} */ this.cancel = cancel; /** * @private */ this.needsStart = true; }; defineProperties(Tween.prototype, { /** * An object with properties for initial values of the tween. The properties of this object are changed during the tween's animation. * @memberof Tween.prototype * * @type {Object} * @readonly */ startObject : { get : function() { return this._startObject; } }, /** * An object with properties for the final values of the tween. * @memberof Tween.prototype * * @type {Object} * @readonly */ stopObject : { get : function() { return this._stopObject; } }, /** * The duration, in seconds, for the tween. The tween is automatically removed from the collection when it stops. * @memberof Tween.prototype * * @type {Number} * @readonly */ duration : { get : function() { return this._duration; } }, /** * The delay, in seconds, before the tween starts animating. * @memberof Tween.prototype * * @type {Number} * @readonly */ delay : { get : function() { return this._delay; } }, /** * Determines the curve for animtion. * @memberof Tween.prototype * * @type {EasingFunction} * @readonly */ easingFunction : { get : function() { return this._easingFunction; } }, /** * The callback to call at each animation update (usually tied to the a rendered frame). * @memberof Tween.prototype * * @type {TweenCollection~TweenUpdateCallback} * @readonly */ update : { get : function() { return this._update; } }, /** * The callback to call when the tween finishes animating. * @memberof Tween.prototype * * @type {TweenCollection~TweenCompleteCallback} * @readonly */ complete : { get : function() { return this._complete; } }, /** * @memberof Tween.prototype * * @private */ tweenjs : { get : function() { return this._tweenjs; } } }); /** * Cancels the tween calling the {@link Tween#cancel} callback if one exists. This * has no effect if the tween finished or was already canceled. */ Tween.prototype.cancelTween = function() { this._tweens.remove(this); }; /** * A collection of tweens for animating properties. Commonly accessed using {@link Scene#tweens}. * * @alias TweenCollection * @constructor * * @private */ var TweenCollection = function() { this._tweens = []; }; defineProperties(TweenCollection.prototype, { /** * The number of tweens in the collection. * @memberof TweenCollection.prototype * * @type {Number} * @readonly */ length : { get : function() { return this._tweens.length; } } }); /** * Creates a tween for animating between two sets of properties. The tween starts animating at the next call to {@link TweenCollection#update}, which * is implicit when {@link Viewer} or {@link CesiumWidget} render the scene. * * @param {Object} [options] Object with the following properties: * @param {Object} options.startObject An object with properties for initial values of the tween. The properties of this object are changed during the tween's animation. * @param {Object} options.stopObject An object with properties for the final values of the tween. * @param {Number} options.duration The duration, in seconds, for the tween. The tween is automatically removed from the collection when it stops. * @param {Number} [options.delay=0.0] The delay, in seconds, before the tween starts animating. * @param {EasingFunction} [options.easingFunction=EasingFunction.LINEAR_NONE] Determines the curve for animtion. * @param {TweenCollection~TweenUpdateCallback} [options.update] The callback to call at each animation update (usually tied to the a rendered frame). * @param {TweenCollection~TweenCompleteCallback} [options.complete] The callback to call when the tween finishes animating. * @param {TweenCollection~TweenCancelledCallback} [options.cancel] The callback to call if the tween is canceled either because {@link Tween#cancelTween} was called or because the tween was removed from the collection. * @returns {Tween} The tween. * * @exception {DeveloperError} options.duration must be positive. */ TweenCollection.prototype.add = function(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); if (!defined(options.startObject) || !defined(options.stopObject)) { throw new DeveloperError('options.startObject and options.stopObject are required.'); } if (!defined(options.duration) || options.duration < 0.0) { throw new DeveloperError('options.duration is required and must be positive.'); } //>>includeEnd('debug'); if (options.duration === 0.0) { if (defined(options.complete)) { options.complete(); } return new Tween(this); } var duration = options.duration / TimeConstants.SECONDS_PER_MILLISECOND; var delayInSeconds = defaultValue(options.delay, 0.0); var delay = delayInSeconds / TimeConstants.SECONDS_PER_MILLISECOND; var easingFunction = defaultValue(options.easingFunction, EasingFunction.LINEAR_NONE); var value = options.startObject; var tweenjs = new TweenJS.Tween(value); tweenjs.to(clone(options.stopObject), duration); tweenjs.delay(delay); tweenjs.easing(easingFunction); if (defined(options.update)) { tweenjs.onUpdate(function() { options.update(value); }); } tweenjs.onComplete(defaultValue(options.complete, null)); tweenjs.repeat(defaultValue(options._repeat, 0.0)); var tween = new Tween(this, tweenjs, options.startObject, options.stopObject, options.duration, delayInSeconds, easingFunction, options.update, options.complete, options.cancel); this._tweens.push(tween); return tween; }; /** * Creates a tween for animating a scalar property on the given object. The tween starts animating at the next call to {@link TweenCollection#update}, which * is implicit when {@link Viewer} or {@link CesiumWidget} render the scene. * * @param {Object} [options] Object with the following properties: * @param {Object} options.object The object containing the property to animate. * @param {String} options.property The name of the property to animate. * @param {Number} options.startValue The initial value. * @param {Number} options.stopValue The final value. * @param {Number} [options.duration=3.0] The duration, in seconds, for the tween. The tween is automatically removed from the collection when it stops. * @param {Number} [options.delay=0.0] The delay, in seconds, before the tween starts animating. * @param {EasingFunction} [options.easingFunction=EasingFunction.LINEAR_NONE] Determines the curve for animtion. * @param {TweenCollection~TweenUpdateCallback} [options.update] The callback to call at each animation update (usually tied to the a rendered frame). * @param {TweenCollection~TweenCompleteCallback} [options.complete] The callback to call when the tween finishes animating. * @param {TweenCollection~TweenCancelledCallback} [options.cancel] The callback to call if the tween is canceled either because {@link Tween#cancelTween} was called or because the tween was removed from the collection. * @returns {Tween} The tween. * * @exception {DeveloperError} options.object must have the specified property. * @exception {DeveloperError} options.duration must be positive. */ TweenCollection.prototype.addProperty = function(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var object = options.object; var property = options.property; var startValue = options.startValue; var stopValue = options.stopValue; //>>includeStart('debug', pragmas.debug); if (!defined(object) || !defined(options.property)) { throw new DeveloperError('options.object and options.property are required.'); } if (!defined(object[property])) { throw new DeveloperError('options.object must have the specified property.'); } if (!defined(startValue) || !defined(stopValue)) { throw new DeveloperError('options.startValue and options.stopValue are required.'); } //>>includeEnd('debug'); function update(value) { object[property] = value.value; } return this.add({ startObject : { value : startValue }, stopObject : { value : stopValue }, duration : defaultValue(options.duration, 3.0), delay : options.delay, easingFunction : options.easingFunction, update : update, complete : options.complete, cancel : options.cancel, _repeat : options._repeat }); }; /** * Creates a tween for animating the alpha of all color uniforms on a {@link Material}. The tween starts animating at the next call to {@link TweenCollection#update}, which * is implicit when {@link Viewer} or {@link CesiumWidget} render the scene. * * @param {Object} [options] Object with the following properties: * @param {Material} options.material The material to animate. * @param {Number} [options.startValue=0.0] The initial alpha value. * @param {Number} [options.stopValue=1.0] The final alpha value. * @param {Number} [options.duration=3.0] The duration, in seconds, for the tween. The tween is automatically removed from the collection when it stops. * @param {Number} [options.delay=0.0] The delay, in seconds, before the tween starts animating. * @param {EasingFunction} [options.easingFunction=EasingFunction.LINEAR_NONE] Determines the curve for animtion. * @param {TweenCollection~TweenUpdateCallback} [options.update] The callback to call at each animation update (usually tied to the a rendered frame). * @param {TweenCollection~TweenCompleteCallback} [options.complete] The callback to call when the tween finishes animating. * @param {TweenCollection~TweenCancelledCallback} [options.cancel] The callback to call if the tween is canceled either because {@link Tween#cancelTween} was called or because the tween was removed from the collection. * @returns {Tween} The tween. * * @exception {DeveloperError} material has no properties with alpha components. * @exception {DeveloperError} options.duration must be positive. */ TweenCollection.prototype.addAlpha = function(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var material = options.material; //>>includeStart('debug', pragmas.debug); if (!defined(material)) { throw new DeveloperError('options.material is required.'); } //>>includeEnd('debug'); var properties = []; for (var property in material.uniforms) { if (material.uniforms.hasOwnProperty(property) && defined(material.uniforms[property]) && defined(material.uniforms[property].alpha)) { properties.push(property); } } //>>includeStart('debug', pragmas.debug); if (properties.length === 0) { throw new DeveloperError('material has no properties with alpha components.'); } //>>includeEnd('debug'); function update(value) { var length = properties.length; for (var i = 0; i < length; ++i) { material.uniforms[properties[i]].alpha = value.alpha; } } return this.add({ startObject : { alpha : defaultValue(options.startValue, 0.0) // Default to fade in }, stopObject : { alpha : defaultValue(options.stopValue, 1.0) }, duration : defaultValue(options.duration, 3.0), delay : options.delay, easingFunction : options.easingFunction, update : update, complete : options.complete, cancel : options.cancel }); }; /** * Creates a tween for animating the offset uniform of a {@link Material}. The tween starts animating at the next call to {@link TweenCollection#update}, which * is implicit when {@link Viewer} or {@link CesiumWidget} render the scene. * * @param {Object} [options] Object with the following properties: * @param {Material} options.material The material to animate. * @param {Number} options.startValue The initial alpha value. * @param {Number} options.stopValue The final alpha value. * @param {Number} [options.duration=3.0] The duration, in seconds, for the tween. The tween is automatically removed from the collection when it stops. * @param {Number} [options.delay=0.0] The delay, in seconds, before the tween starts animating. * @param {EasingFunction} [options.easingFunction=EasingFunction.LINEAR_NONE] Determines the curve for animtion. * @param {TweenCollection~TweenUpdateCallback} [options.update] The callback to call at each animation update (usually tied to the a rendered frame). * @param {TweenCollection~TweenCancelledCallback} [options.cancel] The callback to call if the tween is canceled either because {@link Tween#cancelTween} was called or because the tween was removed from the collection. * @returns {Tween} The tween. * * @exception {DeveloperError} material.uniforms must have an offset property. * @exception {DeveloperError} options.duration must be positive. */ TweenCollection.prototype.addOffsetIncrement = function(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var material = options.material; //>>includeStart('debug', pragmas.debug); if (!defined(material)) { throw new DeveloperError('material is required.'); } if (!defined(material.uniforms.offset)) { throw new DeveloperError('material.uniforms must have an offset property.'); } //>>includeEnd('debug'); var uniforms = material.uniforms; return this.addProperty({ object : uniforms, property : 'offset', startValue : uniforms.offset, stopValue : uniforms.offset + 1, duration : options.duration, delay : options.delay, easingFunction : options.easingFunction, update : options.update, cancel : options.cancel, _repeat : Infinity }); }; /** * Removes a tween from the collection. *
* This calls the {@link Tween#cancel} callback if the tween has one. *
* * @param {Tween} tween The tween to remove. * @returns {Boolean}true
if the tween was removed; false
if the tween was not found in the collection.
*/
TweenCollection.prototype.remove = function(tween) {
if (!defined(tween)) {
return false;
}
var index = this._tweens.indexOf(tween);
if (index !== -1) {
tween.tweenjs.stop();
if (defined(tween.cancel)) {
tween.cancel();
}
this._tweens.splice(index, 1);
return true;
}
return false;
};
/**
* Removes all tweens from the collection.
* * This calls the {@link Tween#cancel} callback for each tween that has one. *
*/ TweenCollection.prototype.removeAll = function() { var tweens = this._tweens; for (var i = 0; i < tweens.length; ++i) { var tween = tweens[i]; tween.tweenjs.stop(); if (defined(tween.cancel)) { tween.cancel(); } } tweens.length = 0; }; /** * Determines whether this collection contains a given tween. * * @param {Tween} tween The tween to check for. * @returns {Boolean}true
if this collection contains the tween, false
otherwise.
*/
TweenCollection.prototype.contains = function(tween) {
return defined(tween) && (this._tweens.indexOf(tween) !== -1);
};
/**
* Returns the tween in the collection at the specified index. Indices are zero-based
* and increase as tweens are added. Removing a tween shifts all tweens after
* it to the left, changing their indices. This function is commonly used to iterate over
* all the tween in the collection.
*
* @param {Number} index The zero-based index of the tween.
* @returns {Tween} The tween at the specified index.
*
* @example
* // Output the duration of all the tweens in the collection.
* var tweens = scene.tweens;
* var length = tweens.length;
* for (var i = 0; i < length; ++i) {
* console.log(tweens.get(i).duration);
* }
*/
TweenCollection.prototype.get = function(index) {
//>>includeStart('debug', pragmas.debug);
if (!defined(index)) {
throw new DeveloperError('index is required.');
}
//>>includeEnd('debug');
return this._tweens[index];
};
/**
* Updates the tweens in the collection to be at the provide time. When a tween finishes, it is removed
* from the collection.
*
* @param {Number} [time=getTimestamp()] The time in seconds. By default tweens are synced to the system clock.
*/
TweenCollection.prototype.update = function(time) {
var tweens = this._tweens;
var i = 0;
time = defined(time) ? time / TimeConstants.SECONDS_PER_MILLISECOND : getTimestamp();
while (i < tweens.length) {
var tween = tweens[i];
var tweenjs = tween.tweenjs;
if (tween.needsStart) {
tween.needsStart = false;
tweenjs.start(time);
} else {
if (tweenjs.update(time)) {
i++;
} else {
tweenjs.stop();
tweens.splice(i, 1);
}
}
}
};
/**
* A function that will execute when a tween completes.
* @callback TweenCollection~TweenCompleteCallback
*/
/**
* A function that will execute when a tween updates.
* @callback TweenCollection~TweenUpdateCallback
*/
/**
* A function that will execute when a tween is cancelled.
* @callback TweenCollection~TweenCancelledCallback
*/
return TweenCollection;
});