TweenCollection.js 21 KB

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