Manager3.html 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5. <title>The source code</title>
  6. <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  7. <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  8. <style type="text/css">
  9. .highlight { display: block; background-color: #ddd; }
  10. </style>
  11. <script type="text/javascript">
  12. function highlight() {
  13. document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
  14. }
  15. </script>
  16. </head>
  17. <body onload="prettyPrint(); highlight();">
  18. <pre class="prettyprint lang-js"><span id='Ext-fx-Manager'>/**
  19. </span> * @class Ext.fx.Manager
  20. * Animation Manager which keeps track of all current animations and manages them on a frame by frame basis.
  21. * @private
  22. * @singleton
  23. */
  24. Ext.define('Ext.fx.Manager', {
  25. /* Begin Definitions */
  26. singleton: true,
  27. requires: ['Ext.util.MixedCollection',
  28. 'Ext.fx.target.Element',
  29. 'Ext.fx.target.ElementCSS',
  30. 'Ext.fx.target.CompositeElement',
  31. 'Ext.fx.target.CompositeElementCSS',
  32. 'Ext.fx.target.Sprite',
  33. 'Ext.fx.target.CompositeSprite',
  34. 'Ext.fx.target.Component'],
  35. mixins: {
  36. queue: 'Ext.fx.Queue'
  37. },
  38. /* End Definitions */
  39. constructor: function() {
  40. this.items = new Ext.util.MixedCollection();
  41. this.mixins.queue.constructor.call(this);
  42. // this.requestAnimFrame = (function() {
  43. // var raf = window.requestAnimationFrame ||
  44. // window.webkitRequestAnimationFrame ||
  45. // window.mozRequestAnimationFrame ||
  46. // window.oRequestAnimationFrame ||
  47. // window.msRequestAnimationFrame;
  48. // if (raf) {
  49. // return function(callback, element) {
  50. // raf(callback);
  51. // };
  52. // }
  53. // else {
  54. // return function(callback, element) {
  55. // window.setTimeout(callback, Ext.fx.Manager.interval);
  56. // };
  57. // }
  58. // })();
  59. },
  60. <span id='Ext-fx-Manager-cfg-interval'> /**
  61. </span> * @cfg {Number} interval Default interval in miliseconds to calculate each frame. Defaults to 16ms (~60fps)
  62. */
  63. interval: 16,
  64. <span id='Ext-fx-Manager-cfg-forceJS'> /**
  65. </span> * @cfg {Boolean} forceJS Force the use of JavaScript-based animation instead of CSS3 animation, even when CSS3
  66. * animation is supported by the browser. This defaults to true currently, as CSS3 animation support is still
  67. * considered experimental at this time, and if used should be thouroughly tested across all targeted browsers.
  68. * @protected
  69. */
  70. forceJS: true,
  71. // @private Target factory
  72. createTarget: function(target) {
  73. var me = this,
  74. useCSS3 = !me.forceJS &amp;&amp; Ext.supports.Transitions,
  75. targetObj;
  76. me.useCSS3 = useCSS3;
  77. if (target) {
  78. // dom element, string or fly
  79. if (target.tagName || Ext.isString(target) || target.isFly) {
  80. target = Ext.get(target);
  81. targetObj = new Ext.fx.target['Element' + (useCSS3 ? 'CSS' : '')](target);
  82. }
  83. // Element
  84. else if (target.dom) {
  85. targetObj = new Ext.fx.target['Element' + (useCSS3 ? 'CSS' : '')](target);
  86. }
  87. // Element Composite
  88. else if (target.isComposite) {
  89. targetObj = new Ext.fx.target['CompositeElement' + (useCSS3 ? 'CSS' : '')](target);
  90. }
  91. // Draw Sprite
  92. else if (target.isSprite) {
  93. targetObj = new Ext.fx.target.Sprite(target);
  94. }
  95. // Draw Sprite Composite
  96. else if (target.isCompositeSprite) {
  97. targetObj = new Ext.fx.target.CompositeSprite(target);
  98. }
  99. // Component
  100. else if (target.isComponent) {
  101. targetObj = new Ext.fx.target.Component(target);
  102. }
  103. else if (target.isAnimTarget) {
  104. return target;
  105. }
  106. else {
  107. return null;
  108. }
  109. me.targets.add(targetObj);
  110. return targetObj;
  111. }
  112. else {
  113. return null;
  114. }
  115. },
  116. <span id='Ext-fx-Manager-method-addAnim'> /**
  117. </span> * Add an Anim to the manager. This is done automatically when an Anim instance is created.
  118. * @param {Ext.fx.Anim} anim
  119. */
  120. addAnim: function(anim) {
  121. var items = this.items,
  122. task = this.task;
  123. // Make sure we use the anim's id, not the anim target's id here. The anim id will be unique on
  124. // each call to addAnim. `anim.target` is the DOM element being targeted, and since multiple animations
  125. // can target a single DOM node concurrently, the target id cannot be assumned to be unique.
  126. items.add(anim.id, anim);
  127. //Ext.log('+ added anim ', anim.id, ', target: ', anim.target.getId(), ', duration: ', anim.duration);
  128. // Start the timer if not already running
  129. if (!task &amp;&amp; items.length) {
  130. task = this.task = {
  131. run: this.runner,
  132. interval: this.interval,
  133. scope: this
  134. };
  135. //Ext.log('---&gt;&gt; Starting task');
  136. Ext.TaskManager.start(task);
  137. }
  138. },
  139. <span id='Ext-fx-Manager-method-removeAnim'> /**
  140. </span> * Remove an Anim from the manager. This is done automatically when an Anim ends.
  141. * @param {Ext.fx.Anim} anim
  142. */
  143. removeAnim: function(anim) {
  144. var me = this,
  145. items = me.items,
  146. task = me.task;
  147. items.removeAtKey(anim.id);
  148. //Ext.log(' X removed anim ', anim.id, ', target: ', anim.target.getId(), ', frames: ', anim.frameCount, ', item count: ', items.length);
  149. // Stop the timer if there are no more managed Anims
  150. if (task &amp;&amp; !items.length) {
  151. //Ext.log('[]--- Stopping task');
  152. Ext.TaskManager.stop(task);
  153. delete me.task;
  154. }
  155. },
  156. <span id='Ext-fx-Manager-method-runner'> /**
  157. </span> * @private
  158. * Runner function being called each frame
  159. */
  160. runner: function() {
  161. var me = this,
  162. items = me.items.getRange(),
  163. i = 0,
  164. len = items.length,
  165. anim;
  166. //Ext.log(' executing anim runner task with ', len, ' items');
  167. me.targetArr = {};
  168. // Single timestamp for all animations this interval
  169. me.timestamp = new Date();
  170. // Loop to start any new animations first before looping to
  171. // execute running animations (which will also include all animations
  172. // started in this loop). This is a subtle difference from simply
  173. // iterating in one loop and starting then running each animation,
  174. // but separating the loops is necessary to ensure that all new animations
  175. // actually kick off prior to existing ones regardless of array order.
  176. // Otherwise in edge cases when there is excess latency in overall
  177. // performance, allowing existing animations to run before new ones can
  178. // lead to dropped frames and subtle race conditions when they are
  179. // interdependent, which is often the case with certain Element fx.
  180. for (; i &lt; len; i++) {
  181. anim = items[i];
  182. if (anim.isReady()) {
  183. //Ext.log(' starting anim ', anim.id, ', target: ', anim.target.id);
  184. me.startAnim(anim);
  185. }
  186. }
  187. for (i = 0; i &lt; len; i++) {
  188. anim = items[i];
  189. if (anim.isRunning()) {
  190. //Ext.log(' running anim ', anim.target.id);
  191. me.runAnim(anim);
  192. //&lt;debug&gt;
  193. } else if (!me.useCSS3) {
  194. // When using CSS3 transitions the animations get paused since they are not
  195. // needed once the transition is handed over to the browser, so we can
  196. // ignore this case. However if we are doing JS animations and something is
  197. // paused here it's possibly unintentional.
  198. //Ext.log(' (i) anim ', anim.id, ' is active but not running...');
  199. //&lt;/debug&gt;
  200. }
  201. }
  202. // Apply all the pending changes to their targets
  203. me.applyPendingAttrs();
  204. },
  205. <span id='Ext-fx-Manager-method-startAnim'> /**
  206. </span> * @private
  207. * Start the individual animation (initialization)
  208. */
  209. startAnim: function(anim) {
  210. anim.start(this.timestamp);
  211. },
  212. <span id='Ext-fx-Manager-method-runAnim'> /**
  213. </span> * @private
  214. * Run the individual animation for this frame
  215. */
  216. runAnim: function(anim) {
  217. if (!anim) {
  218. return;
  219. }
  220. var me = this,
  221. targetId = anim.target.getId(),
  222. useCSS3 = me.useCSS3 &amp;&amp; anim.target.type == 'element',
  223. elapsedTime = me.timestamp - anim.startTime,
  224. lastFrame = (elapsedTime &gt;= anim.duration),
  225. target, o;
  226. target = this.collectTargetData(anim, elapsedTime, useCSS3, lastFrame);
  227. // For CSS3 animation, we need to immediately set the first frame's attributes without any transition
  228. // to get a good initial state, then add the transition properties and set the final attributes.
  229. if (useCSS3) {
  230. //Ext.log(' (i) using CSS3 transitions');
  231. // Flush the collected attributes, without transition
  232. anim.target.setAttr(target.anims[anim.id].attributes, true);
  233. // Add the end frame data
  234. me.collectTargetData(anim, anim.duration, useCSS3, lastFrame);
  235. // Pause the animation so runAnim doesn't keep getting called
  236. anim.paused = true;
  237. target = anim.target.target;
  238. // We only want to attach an event on the last element in a composite
  239. if (anim.target.isComposite) {
  240. target = anim.target.target.last();
  241. }
  242. // Listen for the transitionend event
  243. o = {};
  244. o[Ext.supports.CSS3TransitionEnd] = anim.lastFrame;
  245. o.scope = anim;
  246. o.single = true;
  247. target.on(o);
  248. }
  249. },
  250. <span id='Ext-fx-Manager-method-collectTargetData'> /**
  251. </span> * @private
  252. * Collect target attributes for the given Anim object at the given timestamp
  253. * @param {Ext.fx.Anim} anim The Anim instance
  254. * @param {Number} timestamp Time after the anim's start time
  255. * @param {Boolean} [useCSS3=false] True if using CSS3-based animation, else false
  256. * @param {Boolean} [isLastFrame=false] True if this is the last frame of animation to be run, else false
  257. * @return {Object} The animation target wrapper object containing the passed animation along with the
  258. * new attributes to set on the target's element in the next animation frame.
  259. */
  260. collectTargetData: function(anim, elapsedTime, useCSS3, isLastFrame) {
  261. var targetId = anim.target.getId(),
  262. target = this.targetArr[targetId];
  263. if (!target) {
  264. // Create a thin wrapper around the target so that we can create a link between the
  265. // target element and its associated animations. This is important later when applying
  266. // attributes to the target so that each animation can be independently run with its own
  267. // duration and stopped at any point without affecting other animations for the same target.
  268. target = this.targetArr[targetId] = {
  269. id: targetId,
  270. el: anim.target,
  271. anims: {}
  272. };
  273. }
  274. // This is a wrapper for the animation so that we can also save state along with it,
  275. // including the current elapsed time and lastFrame status. Even though this method only
  276. // adds a single anim object per call, each target element could have multiple animations
  277. // associated with it, which is why the anim is added to the target's `anims` hash by id.
  278. target.anims[anim.id] = {
  279. id: anim.id,
  280. anim: anim,
  281. elapsed: elapsedTime,
  282. isLastFrame: isLastFrame,
  283. // This is the object that gets applied to the target element below in applyPendingAttrs():
  284. attributes: [{
  285. duration: anim.duration,
  286. easing: (useCSS3 &amp;&amp; anim.reverse) ? anim.easingFn.reverse().toCSS3() : anim.easing,
  287. // This is where the magic happens. The anim calculates what its new attributes should
  288. // be based on the current frame and returns those as a hash of values.
  289. attrs: anim.runAnim(elapsedTime)
  290. }]
  291. };
  292. return target;
  293. },
  294. <span id='Ext-fx-Manager-method-applyPendingAttrs'> /**
  295. </span> * @private
  296. * Apply all pending attribute changes to their targets
  297. */
  298. applyPendingAttrs: function() {
  299. var targetArr = this.targetArr,
  300. target, targetId, animWrap, anim, animId;
  301. // Loop through each target
  302. for (targetId in targetArr) {
  303. if (targetArr.hasOwnProperty(targetId)) {
  304. target = targetArr[targetId];
  305. // Each target could have multiple associated animations, so iterate those
  306. for (animId in target.anims) {
  307. if (target.anims.hasOwnProperty(animId)) {
  308. animWrap = target.anims[animId];
  309. anim = animWrap.anim;
  310. // If the animation has valid attributes, set them on the target
  311. if (animWrap.attributes &amp;&amp; anim.isRunning()) {
  312. //Ext.log(' &gt; applying attributes for anim ', animWrap.id, ', target: ', target.id, ', elapsed: ', animWrap.elapsed);
  313. target.el.setAttr(animWrap.attributes, false, animWrap.isLastFrame);
  314. // If this particular anim is at the last frame end it
  315. if (animWrap.isLastFrame) {
  316. //Ext.log(' running last frame for ', animWrap.id, ', target: ', targetId);
  317. anim.lastFrame();
  318. }
  319. }
  320. }
  321. }
  322. }
  323. }
  324. }
  325. });
  326. </pre>
  327. </body>
  328. </html>