PageRenderTime 50ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/touch/src/draw/modifier/Animation.js

https://bitbucket.org/mblomdahl/neonode-mobile
JavaScript | 431 lines | 293 code | 44 blank | 94 comment | 60 complexity | 1d3c45ce3773efb3791e90e827b0b647 MD5 | raw file
  1. /**
  2. * The Animation modifier.
  3. *
  4. * Sencha Touch allows users to use transitional animation on sprites. Simply set the duration
  5. * and easing in the animation modifier, then all the changes to the sprites will be animated.
  6. *
  7. * Also, you can use different durations and easing functions on different attributes by using
  8. * {@link #customDuration} and {@link #customEasings}.
  9. *
  10. * By default, an animation modifier will be created during the initialization of a sprite.
  11. * You can get the modifier of `sprite` by `sprite.fx`.
  12. *
  13. */
  14. Ext.define("Ext.draw.modifier.Animation", {
  15. mixins: {
  16. observable: 'Ext.mixin.Observable'
  17. },
  18. requires: [
  19. 'Ext.draw.TimingFunctions',
  20. 'Ext.draw.Animator'
  21. ],
  22. extend: 'Ext.draw.modifier.Modifier',
  23. alias: 'modifier.animation',
  24. config: {
  25. /**
  26. * @cfg {Function} easing
  27. * Default easing function.
  28. */
  29. easing: function (x) {
  30. return x;
  31. },
  32. /**
  33. * @cfg {Number} duration
  34. * Default duration time (ms).
  35. */
  36. duration: 0,
  37. /**
  38. * @cfg {Object} customEasings Overrides the default easing function for defined attributes.
  39. */
  40. customEasings: {},
  41. /**
  42. * @cfg {Object} customDuration Overrides the default duration for defined attributes.
  43. */
  44. customDuration: {}
  45. },
  46. constructor: function () {
  47. this.anyAnimation = false;
  48. this.anySpecialAnimations = false;
  49. this.animating = 0;
  50. this.animatingPool = [];
  51. this.callSuper(arguments);
  52. },
  53. /**
  54. * @inheritdoc
  55. */
  56. prepareAttributes: function (attr) {
  57. if (!attr.hasOwnProperty('timers')) {
  58. attr.animating = false;
  59. attr.timers = {};
  60. attr.animationOriginal = Ext.Object.chain(attr);
  61. attr.animationOriginal.upperLevel = attr;
  62. }
  63. if (this._previous) {
  64. this._previous.prepareAttributes(attr.animationOriginal);
  65. }
  66. },
  67. updateSprite: function (sprite) {
  68. // Apply the config that was configured in the sprite.
  69. this.setConfig(sprite.config.fx);
  70. },
  71. updateDuration: function (duration) {
  72. this.anyAnimation = duration > 0;
  73. },
  74. applyEasing: function (easing) {
  75. if (typeof easing === 'string') {
  76. return Ext.draw.TimingFunctions.easingMap[easing];
  77. } else {
  78. return easing;
  79. }
  80. },
  81. applyCustomEasings: function (newCustomEasing, oldCustomEasing) {
  82. oldCustomEasing = oldCustomEasing || {};
  83. var attr, attrs, easing, i, ln;
  84. for (attr in newCustomEasing) {
  85. easing = newCustomEasing[attr];
  86. attrs = attr.split(',');
  87. if (typeof easing === 'string') {
  88. easing = Ext.draw.TimingFunctions.easingMap[easing];
  89. }
  90. for (i = 0, ln = attrs.length; i < ln; i++) {
  91. oldCustomEasing[attrs[i]] = easing;
  92. }
  93. }
  94. return oldCustomEasing;
  95. },
  96. /**
  97. * Set special easings on the given attributes.
  98. * @param attrs The source attributes.
  99. * @param easing The special easings.
  100. */
  101. setEasingOn: function (attrs, easing) {
  102. attrs = Ext.Array.from(attrs).slice();
  103. var customEasing = {},
  104. i = 0,
  105. ln = attrs.length;
  106. for (; i < ln; i++) {
  107. customEasing[attrs[i]] = easing;
  108. }
  109. this.setDurationEasings(customEasing);
  110. },
  111. /**
  112. * Remove special easings on the given attributes.
  113. * @param attrs The source attributes.
  114. */
  115. clearEasingOn: function (attrs) {
  116. attrs = Ext.Array.from(attrs, true);
  117. var i = 0, ln = attrs.length;
  118. for (; i < ln; i++) {
  119. delete this._customEasing[attrs[i]];
  120. }
  121. },
  122. applyCustomDuration: function (newCustomDuration, oldCustomDuration) {
  123. oldCustomDuration = oldCustomDuration || {};
  124. var attr, duration, attrs, i, ln, anySpecialAnimations = this.anySpecialAnimations;
  125. for (attr in newCustomDuration) {
  126. duration = newCustomDuration[attr];
  127. attrs = attr.split(',');
  128. anySpecialAnimations = true;
  129. for (i = 0, ln = attrs.length; i < ln; i++) {
  130. oldCustomDuration[attrs[i]] = duration;
  131. }
  132. }
  133. this.anySpecialAnimations = anySpecialAnimations;
  134. return oldCustomDuration;
  135. },
  136. /**
  137. * Set special duration on the given attributes.
  138. * @param attrs The source attributes.
  139. * @param duration The special duration.
  140. */
  141. setDurationOn: function (attrs, duration) {
  142. attrs = Ext.Array.from(attrs).slice();
  143. var customDurations = {},
  144. i = 0,
  145. ln = attrs.length;
  146. for (; i < ln; i++) {
  147. customDurations[attrs[i]] = duration;
  148. }
  149. this.setCustomDuration(customDurations);
  150. },
  151. /**
  152. * Remove special easings on the given attributes.
  153. * @param attrs The source attributes.
  154. */
  155. clearDurationOn: function (attrs) {
  156. attrs = Ext.Array.from(attrs, true);
  157. var i = 0, ln = attrs.length;
  158. for (; i < ln; i++) {
  159. delete this._customDuration[attrs[i]];
  160. }
  161. },
  162. /**
  163. * @private
  164. * Initializes Animator for the animation.
  165. * @param attributes The source attributes.
  166. * @param animating The animating flag.
  167. */
  168. setAnimating: function (attributes, animating) {
  169. var me = this,
  170. i, j;
  171. if (attributes.animating !== animating) {
  172. attributes.animating = animating;
  173. if (animating) {
  174. me.animatingPool.push(attributes);
  175. if (me.animating === 0) {
  176. Ext.draw.Animator.add(me);
  177. }
  178. me.animating++;
  179. } else {
  180. for (i = 0, j = 0; i < me.animatingPool.length; i++) {
  181. if (me.animatingPool[i] !== attributes) {
  182. me.animatingPool[j++] = me.animatingPool[i];
  183. }
  184. }
  185. me.animating = me.animatingPool.length = j;
  186. }
  187. }
  188. },
  189. /**
  190. * @private
  191. * Set the attr with given easing and duration.
  192. * @param {Object} attr The attributes collection.
  193. * @param {Object} changes The changes that popped up from lower modifier.
  194. * @return {Object} The changes to pop up.
  195. */
  196. setAttrs: function (attr, changes) {
  197. var timers = attr.timers,
  198. parsers = this._sprite.self.def._animationProcessors,
  199. defaultEasing = this._easing,
  200. defaultDuration = this._duration,
  201. customDuration = this._customDuration,
  202. customEasings = this._customEasings,
  203. anySpecial = this.anySpecialAnimations,
  204. any = this.anyAnimation || anySpecial,
  205. original = attr.animationOriginal,
  206. ignite = false,
  207. timer, name, newValue, startValue, parser, easing, duration;
  208. if (!any) {
  209. // If there is no animation enabled
  210. // When applying changes to attributes, simply stop current animation
  211. // and set the value.
  212. for (name in changes) {
  213. if (attr[name] === changes[name]) {
  214. delete changes[name];
  215. } else {
  216. attr[name] = changes[name];
  217. }
  218. delete original[name];
  219. delete timers[name];
  220. }
  221. return changes;
  222. } else {
  223. // If any animation
  224. for (name in changes) {
  225. newValue = changes[name];
  226. startValue = attr[name];
  227. if (newValue !== startValue && any && startValue !== undefined && startValue !== null && (parser = parsers[name])) {
  228. // If this property is animating.
  229. // Figure out the desired duration and easing.
  230. easing = defaultEasing;
  231. duration = defaultDuration;
  232. if (anySpecial) {
  233. // Deducing the easing function and duration
  234. if (name in customEasings) {
  235. easing = customEasings[name];
  236. }
  237. if (name in customDuration) {
  238. duration = customDuration[name];
  239. }
  240. }
  241. // If the property is animating
  242. if (duration) {
  243. if (!timers[name]) {
  244. timers[name] = {};
  245. }
  246. timer = timers[name];
  247. timer.start = 0;
  248. timer.easing = easing;
  249. timer.duration = duration;
  250. timer.compute = parser.compute;
  251. timer.serve = parser.serve || Ext.draw.Draw.reflectFn;
  252. if (parser.parseInitial) {
  253. var initial = parser.parseInitial(startValue, newValue);
  254. timer.source = initial[0];
  255. timer.target = initial[1];
  256. } else if (parser.parse) {
  257. timer.source = parser.parse(startValue);
  258. timer.target = parser.parse(newValue);
  259. } else {
  260. timer.source = startValue;
  261. timer.target = newValue;
  262. }
  263. // The animation started. Change to originalVal.
  264. timers[name] = timer;
  265. original[name] = newValue;
  266. delete changes[name];
  267. ignite = true;
  268. continue;
  269. } else {
  270. delete original[name];
  271. }
  272. } else {
  273. delete original[name];
  274. }
  275. // If the property is not animating.
  276. delete timers[name];
  277. }
  278. }
  279. if (ignite && !attr.animating) {
  280. this.setAnimating(attr, true);
  281. }
  282. return changes;
  283. },
  284. /**
  285. * @private
  286. *
  287. * Update attributes to current value according to current animation time.
  288. * This method will not effect the values of lower layers, but may delete a
  289. * value from it.
  290. * @param attr The source attributes.
  291. * @return {Object} the changes to popup.
  292. */
  293. updateAttributes: function (attr) {
  294. if (!attr.animating) {
  295. return {};
  296. }
  297. var changes = {}, change,
  298. any = false,
  299. original = attr.animationOriginal,
  300. timers = attr.timers,
  301. now = Ext.draw.Animator.animationTime(),
  302. name, timer, delta;
  303. // If updated in the same frame, return.
  304. if (attr.lastUpdate === now) {
  305. return {};
  306. }
  307. for (name in timers) {
  308. timer = timers[name];
  309. if (!timer.start) {
  310. timer.start = now;
  311. delta = 0;
  312. } else {
  313. delta = (now - timer.start) / timer.duration;
  314. }
  315. if (delta >= 1) {
  316. changes[name] = original[name];
  317. delete original[name];
  318. delete timers[name];
  319. } else {
  320. changes[name] = timer.serve(timer.compute(timer.source, timer.target, timer.easing(delta), attr[name]));
  321. any = true;
  322. }
  323. }
  324. attr.lastUpdate = now;
  325. this.setAnimating(attr, any);
  326. return changes;
  327. },
  328. /**
  329. * @inheritdoc
  330. */
  331. pushDown: function (attr, changes) {
  332. changes = Ext.draw.modifier.Modifier.prototype.pushDown.call(this, attr.animationOriginal, changes);
  333. return this.setAttrs(attr, changes);
  334. },
  335. /**
  336. * @inheritdoc
  337. */
  338. popUp: function (attr, changes) {
  339. attr = attr.upperLevel;
  340. changes = this.setAttrs(attr, changes);
  341. if (this._next) {
  342. return this._next.popUp(attr, changes);
  343. } else {
  344. return Ext.apply(attr, changes);
  345. }
  346. },
  347. // This is called as an animated object in `Ext.draw.Animator`.
  348. step: function () {
  349. var me = this,
  350. pool = me.animatingPool.slice(),
  351. attributes,
  352. i, ln;
  353. for (i = 0, ln = pool.length; i < ln; i++) {
  354. attributes = pool[i];
  355. var changes = this.updateAttributes(attributes),
  356. name;
  357. // Looking for anything in changes
  358. //noinspection LoopStatementThatDoesntLoopJS
  359. for (name in changes) {
  360. if (this._next) {
  361. this._next.popUp(attributes, changes);
  362. }
  363. break;
  364. }
  365. }
  366. },
  367. /**
  368. * Stop all animations effected by this modifier
  369. */
  370. stop: function () {
  371. this.step();
  372. var me = this,
  373. pool = me.animatingPool,
  374. i, ln;
  375. for (i = 0, ln = pool.length; i < ln; i++) {
  376. pool[i].animating = false;
  377. }
  378. me.animatingPool.length = 0;
  379. me.animating = 0;
  380. Ext.draw.Animator.remove(me);
  381. },
  382. destroy: function () {
  383. var me = this;
  384. me.animatingPool.length = 0;
  385. me.animating = 0;
  386. }
  387. });