TweenCollection.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. import clone from '../Core/clone.js';
  2. import defaultValue from '../Core/defaultValue.js';
  3. import defined from '../Core/defined.js';
  4. import defineProperties from '../Core/defineProperties.js';
  5. import DeveloperError from '../Core/DeveloperError.js';
  6. import EasingFunction from '../Core/EasingFunction.js';
  7. import getTimestamp from '../Core/getTimestamp.js';
  8. import TimeConstants from '../Core/TimeConstants.js';
  9. import TweenJS from '../ThirdParty/Tween.js';
  10. /**
  11. * A tween is an animation that interpolates the properties of two objects using an {@link EasingFunction}. Create
  12. * one using {@link Scene#tweens} and {@link TweenCollection#add} and related add functions.
  13. *
  14. * @alias Tween
  15. * @constructor
  16. *
  17. * @private
  18. */
  19. function Tween(tweens, tweenjs, startObject, stopObject, duration, delay, easingFunction, update, complete, cancel) {
  20. this._tweens = tweens;
  21. this._tweenjs = tweenjs;
  22. this._startObject = clone(startObject);
  23. this._stopObject = clone(stopObject);
  24. this._duration = duration;
  25. this._delay = delay;
  26. this._easingFunction = easingFunction;
  27. this._update = update;
  28. this._complete = complete;
  29. /**
  30. * The callback to call if the tween is canceled either because {@link Tween#cancelTween}
  31. * was called or because the tween was removed from the collection.
  32. *
  33. * @type {TweenCollection~TweenCancelledCallback}
  34. */
  35. this.cancel = cancel;
  36. /**
  37. * @private
  38. */
  39. this.needsStart = true;
  40. }
  41. defineProperties(Tween.prototype, {
  42. /**
  43. * An object with properties for initial values of the tween. The properties of this object are changed during the tween's animation.
  44. * @memberof Tween.prototype
  45. *
  46. * @type {Object}
  47. * @readonly
  48. */
  49. startObject : {
  50. get : function() {
  51. return this._startObject;
  52. }
  53. },
  54. /**
  55. * An object with properties for the final values of the tween.
  56. * @memberof Tween.prototype
  57. *
  58. * @type {Object}
  59. * @readonly
  60. */
  61. stopObject : {
  62. get : function() {
  63. return this._stopObject;
  64. }
  65. },
  66. /**
  67. * The duration, in seconds, for the tween. The tween is automatically removed from the collection when it stops.
  68. * @memberof Tween.prototype
  69. *
  70. * @type {Number}
  71. * @readonly
  72. */
  73. duration : {
  74. get : function() {
  75. return this._duration;
  76. }
  77. },
  78. /**
  79. * The delay, in seconds, before the tween starts animating.
  80. * @memberof Tween.prototype
  81. *
  82. * @type {Number}
  83. * @readonly
  84. */
  85. delay : {
  86. get : function() {
  87. return this._delay;
  88. }
  89. },
  90. /**
  91. * Determines the curve for animtion.
  92. * @memberof Tween.prototype
  93. *
  94. * @type {EasingFunction}
  95. * @readonly
  96. */
  97. easingFunction : {
  98. get : function() {
  99. return this._easingFunction;
  100. }
  101. },
  102. /**
  103. * The callback to call at each animation update (usually tied to the a rendered frame).
  104. * @memberof Tween.prototype
  105. *
  106. * @type {TweenCollection~TweenUpdateCallback}
  107. * @readonly
  108. */
  109. update : {
  110. get : function() {
  111. return this._update;
  112. }
  113. },
  114. /**
  115. * The callback to call when the tween finishes animating.
  116. * @memberof Tween.prototype
  117. *
  118. * @type {TweenCollection~TweenCompleteCallback}
  119. * @readonly
  120. */
  121. complete : {
  122. get : function() {
  123. return this._complete;
  124. }
  125. },
  126. /**
  127. * @memberof Tween.prototype
  128. *
  129. * @private
  130. */
  131. tweenjs : {
  132. get : function() {
  133. return this._tweenjs;
  134. }
  135. }
  136. });
  137. /**
  138. * Cancels the tween calling the {@link Tween#cancel} callback if one exists. This
  139. * has no effect if the tween finished or was already canceled.
  140. */
  141. Tween.prototype.cancelTween = function() {
  142. this._tweens.remove(this);
  143. };
  144. /**
  145. * A collection of tweens for animating properties. Commonly accessed using {@link Scene#tweens}.
  146. *
  147. * @alias TweenCollection
  148. * @constructor
  149. *
  150. * @private
  151. */
  152. function TweenCollection() {
  153. this._tweens = [];
  154. }
  155. defineProperties(TweenCollection.prototype, {
  156. /**
  157. * The number of tweens in the collection.
  158. * @memberof TweenCollection.prototype
  159. *
  160. * @type {Number}
  161. * @readonly
  162. */
  163. length : {
  164. get : function() {
  165. return this._tweens.length;
  166. }
  167. }
  168. });
  169. /**
  170. * Creates a tween for animating between two sets of properties. The tween starts animating at the next call to {@link TweenCollection#update}, which
  171. * is implicit when {@link Viewer} or {@link CesiumWidget} render the scene.
  172. *
  173. * @param {Object} [options] Object with the following properties:
  174. * @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.
  175. * @param {Object} options.stopObject An object with properties for the final values of the tween.
  176. * @param {Number} options.duration The duration, in seconds, for the tween. The tween is automatically removed from the collection when it stops.
  177. * @param {Number} [options.delay=0.0] The delay, in seconds, before the tween starts animating.
  178. * @param {EasingFunction} [options.easingFunction=EasingFunction.LINEAR_NONE] Determines the curve for animtion.
  179. * @param {TweenCollection~TweenUpdateCallback} [options.update] The callback to call at each animation update (usually tied to the a rendered frame).
  180. * @param {TweenCollection~TweenCompleteCallback} [options.complete] The callback to call when the tween finishes animating.
  181. * @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.
  182. * @returns {Tween} The tween.
  183. *
  184. * @exception {DeveloperError} options.duration must be positive.
  185. */
  186. TweenCollection.prototype.add = function(options) {
  187. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  188. //>>includeStart('debug', pragmas.debug);
  189. if (!defined(options.startObject) || !defined(options.stopObject)) {
  190. throw new DeveloperError('options.startObject and options.stopObject are required.');
  191. }
  192. if (!defined(options.duration) || options.duration < 0.0) {
  193. throw new DeveloperError('options.duration is required and must be positive.');
  194. }
  195. //>>includeEnd('debug');
  196. if (options.duration === 0.0) {
  197. if (defined(options.complete)) {
  198. options.complete();
  199. }
  200. return new Tween(this);
  201. }
  202. var duration = options.duration / TimeConstants.SECONDS_PER_MILLISECOND;
  203. var delayInSeconds = defaultValue(options.delay, 0.0);
  204. var delay = delayInSeconds / TimeConstants.SECONDS_PER_MILLISECOND;
  205. var easingFunction = defaultValue(options.easingFunction, EasingFunction.LINEAR_NONE);
  206. var value = options.startObject;
  207. var tweenjs = new TweenJS.Tween(value);
  208. tweenjs.to(clone(options.stopObject), duration);
  209. tweenjs.delay(delay);
  210. tweenjs.easing(easingFunction);
  211. if (defined(options.update)) {
  212. tweenjs.onUpdate(function() {
  213. options.update(value);
  214. });
  215. }
  216. tweenjs.onComplete(defaultValue(options.complete, null));
  217. tweenjs.repeat(defaultValue(options._repeat, 0.0));
  218. var tween = new Tween(this, tweenjs, options.startObject, options.stopObject, options.duration, delayInSeconds, easingFunction, options.update, options.complete, options.cancel);
  219. this._tweens.push(tween);
  220. return tween;
  221. };
  222. /**
  223. * 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
  224. * is implicit when {@link Viewer} or {@link CesiumWidget} render the scene.
  225. *
  226. * @param {Object} [options] Object with the following properties:
  227. * @param {Object} options.object The object containing the property to animate.
  228. * @param {String} options.property The name of the property to animate.
  229. * @param {Number} options.startValue The initial value.
  230. * @param {Number} options.stopValue The final value.
  231. * @param {Number} [options.duration=3.0] The duration, in seconds, for the tween. The tween is automatically removed from the collection when it stops.
  232. * @param {Number} [options.delay=0.0] The delay, in seconds, before the tween starts animating.
  233. * @param {EasingFunction} [options.easingFunction=EasingFunction.LINEAR_NONE] Determines the curve for animtion.
  234. * @param {TweenCollection~TweenUpdateCallback} [options.update] The callback to call at each animation update (usually tied to the a rendered frame).
  235. * @param {TweenCollection~TweenCompleteCallback} [options.complete] The callback to call when the tween finishes animating.
  236. * @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.
  237. * @returns {Tween} The tween.
  238. *
  239. * @exception {DeveloperError} options.object must have the specified property.
  240. * @exception {DeveloperError} options.duration must be positive.
  241. */
  242. TweenCollection.prototype.addProperty = function(options) {
  243. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  244. var object = options.object;
  245. var property = options.property;
  246. var startValue = options.startValue;
  247. var stopValue = options.stopValue;
  248. //>>includeStart('debug', pragmas.debug);
  249. if (!defined(object) || !defined(options.property)) {
  250. throw new DeveloperError('options.object and options.property are required.');
  251. }
  252. if (!defined(object[property])) {
  253. throw new DeveloperError('options.object must have the specified property.');
  254. }
  255. if (!defined(startValue) || !defined(stopValue)) {
  256. throw new DeveloperError('options.startValue and options.stopValue are required.');
  257. }
  258. //>>includeEnd('debug');
  259. function update(value) {
  260. object[property] = value.value;
  261. }
  262. return this.add({
  263. startObject : {
  264. value : startValue
  265. },
  266. stopObject : {
  267. value : stopValue
  268. },
  269. duration : defaultValue(options.duration, 3.0),
  270. delay : options.delay,
  271. easingFunction : options.easingFunction,
  272. update : update,
  273. complete : options.complete,
  274. cancel : options.cancel,
  275. _repeat : options._repeat
  276. });
  277. };
  278. /**
  279. * 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
  280. * is implicit when {@link Viewer} or {@link CesiumWidget} render the scene.
  281. *
  282. * @param {Object} [options] Object with the following properties:
  283. * @param {Material} options.material The material to animate.
  284. * @param {Number} [options.startValue=0.0] The initial alpha value.
  285. * @param {Number} [options.stopValue=1.0] The final alpha value.
  286. * @param {Number} [options.duration=3.0] The duration, in seconds, for the tween. The tween is automatically removed from the collection when it stops.
  287. * @param {Number} [options.delay=0.0] The delay, in seconds, before the tween starts animating.
  288. * @param {EasingFunction} [options.easingFunction=EasingFunction.LINEAR_NONE] Determines the curve for animtion.
  289. * @param {TweenCollection~TweenUpdateCallback} [options.update] The callback to call at each animation update (usually tied to the a rendered frame).
  290. * @param {TweenCollection~TweenCompleteCallback} [options.complete] The callback to call when the tween finishes animating.
  291. * @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.
  292. * @returns {Tween} The tween.
  293. *
  294. * @exception {DeveloperError} material has no properties with alpha components.
  295. * @exception {DeveloperError} options.duration must be positive.
  296. */
  297. TweenCollection.prototype.addAlpha = function(options) {
  298. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  299. var material = options.material;
  300. //>>includeStart('debug', pragmas.debug);
  301. if (!defined(material)) {
  302. throw new DeveloperError('options.material is required.');
  303. }
  304. //>>includeEnd('debug');
  305. var properties = [];
  306. for (var property in material.uniforms) {
  307. if (material.uniforms.hasOwnProperty(property) &&
  308. defined(material.uniforms[property]) &&
  309. defined(material.uniforms[property].alpha)) {
  310. properties.push(property);
  311. }
  312. }
  313. //>>includeStart('debug', pragmas.debug);
  314. if (properties.length === 0) {
  315. throw new DeveloperError('material has no properties with alpha components.');
  316. }
  317. //>>includeEnd('debug');
  318. function update(value) {
  319. var length = properties.length;
  320. for (var i = 0; i < length; ++i) {
  321. material.uniforms[properties[i]].alpha = value.alpha;
  322. }
  323. }
  324. return this.add({
  325. startObject : {
  326. alpha : defaultValue(options.startValue, 0.0) // Default to fade in
  327. },
  328. stopObject : {
  329. alpha : defaultValue(options.stopValue, 1.0)
  330. },
  331. duration : defaultValue(options.duration, 3.0),
  332. delay : options.delay,
  333. easingFunction : options.easingFunction,
  334. update : update,
  335. complete : options.complete,
  336. cancel : options.cancel
  337. });
  338. };
  339. /**
  340. * 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
  341. * is implicit when {@link Viewer} or {@link CesiumWidget} render the scene.
  342. *
  343. * @param {Object} [options] Object with the following properties:
  344. * @param {Material} options.material The material to animate.
  345. * @param {Number} options.startValue The initial alpha value.
  346. * @param {Number} options.stopValue The final alpha value.
  347. * @param {Number} [options.duration=3.0] The duration, in seconds, for the tween. The tween is automatically removed from the collection when it stops.
  348. * @param {Number} [options.delay=0.0] The delay, in seconds, before the tween starts animating.
  349. * @param {EasingFunction} [options.easingFunction=EasingFunction.LINEAR_NONE] Determines the curve for animtion.
  350. * @param {TweenCollection~TweenUpdateCallback} [options.update] The callback to call at each animation update (usually tied to the a rendered frame).
  351. * @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.
  352. * @returns {Tween} The tween.
  353. *
  354. * @exception {DeveloperError} material.uniforms must have an offset property.
  355. * @exception {DeveloperError} options.duration must be positive.
  356. */
  357. TweenCollection.prototype.addOffsetIncrement = function(options) {
  358. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  359. var material = options.material;
  360. //>>includeStart('debug', pragmas.debug);
  361. if (!defined(material)) {
  362. throw new DeveloperError('material is required.');
  363. }
  364. if (!defined(material.uniforms.offset)) {
  365. throw new DeveloperError('material.uniforms must have an offset property.');
  366. }
  367. //>>includeEnd('debug');
  368. var uniforms = material.uniforms;
  369. return this.addProperty({
  370. object : uniforms,
  371. property : 'offset',
  372. startValue : uniforms.offset,
  373. stopValue : uniforms.offset + 1,
  374. duration : options.duration,
  375. delay : options.delay,
  376. easingFunction : options.easingFunction,
  377. update : options.update,
  378. cancel : options.cancel,
  379. _repeat : Infinity
  380. });
  381. };
  382. /**
  383. * Removes a tween from the collection.
  384. * <p>
  385. * This calls the {@link Tween#cancel} callback if the tween has one.
  386. * </p>
  387. *
  388. * @param {Tween} tween The tween to remove.
  389. * @returns {Boolean} <code>true</code> if the tween was removed; <code>false</code> if the tween was not found in the collection.
  390. */
  391. TweenCollection.prototype.remove = function(tween) {
  392. if (!defined(tween)) {
  393. return false;
  394. }
  395. var index = this._tweens.indexOf(tween);
  396. if (index !== -1) {
  397. tween.tweenjs.stop();
  398. if (defined(tween.cancel)) {
  399. tween.cancel();
  400. }
  401. this._tweens.splice(index, 1);
  402. return true;
  403. }
  404. return false;
  405. };
  406. /**
  407. * Removes all tweens from the collection.
  408. * <p>
  409. * This calls the {@link Tween#cancel} callback for each tween that has one.
  410. * </p>
  411. */
  412. TweenCollection.prototype.removeAll = function() {
  413. var tweens = this._tweens;
  414. for (var i = 0; i < tweens.length; ++i) {
  415. var tween = tweens[i];
  416. tween.tweenjs.stop();
  417. if (defined(tween.cancel)) {
  418. tween.cancel();
  419. }
  420. }
  421. tweens.length = 0;
  422. };
  423. /**
  424. * Determines whether this collection contains a given tween.
  425. *
  426. * @param {Tween} tween The tween to check for.
  427. * @returns {Boolean} <code>true</code> if this collection contains the tween, <code>false</code> otherwise.
  428. */
  429. TweenCollection.prototype.contains = function(tween) {
  430. return defined(tween) && (this._tweens.indexOf(tween) !== -1);
  431. };
  432. /**
  433. * Returns the tween in the collection at the specified index. Indices are zero-based
  434. * and increase as tweens are added. Removing a tween shifts all tweens after
  435. * it to the left, changing their indices. This function is commonly used to iterate over
  436. * all the tween in the collection.
  437. *
  438. * @param {Number} index The zero-based index of the tween.
  439. * @returns {Tween} The tween at the specified index.
  440. *
  441. * @example
  442. * // Output the duration of all the tweens in the collection.
  443. * var tweens = scene.tweens;
  444. * var length = tweens.length;
  445. * for (var i = 0; i < length; ++i) {
  446. * console.log(tweens.get(i).duration);
  447. * }
  448. */
  449. TweenCollection.prototype.get = function(index) {
  450. //>>includeStart('debug', pragmas.debug);
  451. if (!defined(index)) {
  452. throw new DeveloperError('index is required.');
  453. }
  454. //>>includeEnd('debug');
  455. return this._tweens[index];
  456. };
  457. /**
  458. * Updates the tweens in the collection to be at the provide time. When a tween finishes, it is removed
  459. * from the collection.
  460. *
  461. * @param {Number} [time=getTimestamp()] The time in seconds. By default tweens are synced to the system clock.
  462. */
  463. TweenCollection.prototype.update = function(time) {
  464. var tweens = this._tweens;
  465. var i = 0;
  466. time = defined(time) ? time / TimeConstants.SECONDS_PER_MILLISECOND : getTimestamp();
  467. while (i < tweens.length) {
  468. var tween = tweens[i];
  469. var tweenjs = tween.tweenjs;
  470. if (tween.needsStart) {
  471. tween.needsStart = false;
  472. tweenjs.start(time);
  473. } else if (tweenjs.update(time)) {
  474. i++;
  475. } else {
  476. tweenjs.stop();
  477. tweens.splice(i, 1);
  478. }
  479. }
  480. };
  481. /**
  482. * A function that will execute when a tween completes.
  483. * @callback TweenCollection~TweenCompleteCallback
  484. */
  485. /**
  486. * A function that will execute when a tween updates.
  487. * @callback TweenCollection~TweenUpdateCallback
  488. */
  489. /**
  490. * A function that will execute when a tween is cancelled.
  491. * @callback TweenCollection~TweenCancelledCallback
  492. */
  493. export default TweenCollection;