You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

589 lines
18 KiB
JavaScript

import clone from "../Core/clone.js";
import defaultValue from "../Core/defaultValue.js";
import defined from "../Core/defined.js";
import DeveloperError from "../Core/DeveloperError.js";
import EasingFunction from "../Core/EasingFunction.js";
import getTimestamp from "../Core/getTimestamp.js";
import TimeConstants from "../Core/TimeConstants.js";
import TweenJS from "../ThirdParty/Tween.js";
/**
* 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
*/
function Tween(
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;
}
Object.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
*/
function TweenCollection() {
this._tweens = [];
}
Object.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.
* <p>
* This calls the {@link Tween#cancel} callback if the tween has one.
* </p>
*
* @param {Tween} tween The tween to remove.
* @returns {Boolean} <code>true</code> if the tween was removed; <code>false</code> 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.
* <p>
* This calls the {@link Tween#cancel} callback for each tween that has one.
* </p>
*/
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} <code>true</code> if this collection contains the tween, <code>false</code> 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
*/
export default TweenCollection;