2 * @class Ext.fx.Manager
3 * Animation Manager which keeps track of all current animations and manages them on a frame by frame basis.
8 Ext.define('Ext.fx.Manager', {
10 /* Begin Definitions */
14 requires: ['Ext.util.MixedCollection',
15 'Ext.fx.target.Element',
16 'Ext.fx.target.CompositeElement',
17 'Ext.fx.target.Sprite',
18 'Ext.fx.target.CompositeSprite',
19 'Ext.fx.target.Component'],
27 constructor: function() {
28 this.items = Ext.create('Ext.util.MixedCollection');
29 this.mixins.queue.constructor.call(this);
31 // this.requestAnimFrame = (function() {
32 // var raf = window.requestAnimationFrame ||
33 // window.webkitRequestAnimationFrame ||
34 // window.mozRequestAnimationFrame ||
35 // window.oRequestAnimationFrame ||
36 // window.msRequestAnimationFrame;
38 // return function(callback, element) {
43 // return function(callback, element) {
44 // window.setTimeout(callback, Ext.fx.Manager.interval);
51 * @cfg {Number} interval Default interval in miliseconds to calculate each frame. Defaults to 16ms (~60fps)
56 * @cfg {Boolean} forceJS Turn off to not use CSS3 transitions when they are available
60 // @private Target factory
61 createTarget: function(target) {
63 useCSS3 = !me.forceJS && Ext.supports.Transitions,
69 if (Ext.isString(target)) {
70 target = Ext.get(target);
73 if (target && target.tagName) {
74 target = Ext.get(target);
75 targetObj = Ext.create('Ext.fx.target.' + 'Element' + (useCSS3 ? 'CSS' : ''), target);
76 me.targets.add(targetObj);
79 if (Ext.isObject(target)) {
82 targetObj = Ext.create('Ext.fx.target.' + 'Element' + (useCSS3 ? 'CSS' : ''), target);
85 else if (target.isComposite) {
86 targetObj = Ext.create('Ext.fx.target.' + 'CompositeElement' + (useCSS3 ? 'CSS' : ''), target);
89 else if (target.isSprite) {
90 targetObj = Ext.create('Ext.fx.target.Sprite', target);
92 // Draw Sprite Composite
93 else if (target.isCompositeSprite) {
94 targetObj = Ext.create('Ext.fx.target.CompositeSprite', target);
97 else if (target.isComponent) {
98 targetObj = Ext.create('Ext.fx.target.Component', target);
100 else if (target.isAnimTarget) {
106 me.targets.add(targetObj);
115 * Add an Anim to the manager. This is done automatically when an Anim instance is created.
116 * @param {Ext.fx.Anim} anim
118 addAnim: function(anim) {
119 var items = this.items,
124 // if (items.length) {
127 // me.requestAnimFrame(cb);
136 // Start the timer if not already running
137 if (!task && items.length) {
140 interval: this.interval,
143 Ext.TaskManager.start(task);
146 // //Start the timer if not already running
147 // if (!me.task && items.length) {
148 // me.requestAnimFrame(cb);
153 * Remove an Anim from the manager. This is done automatically when an Anim ends.
154 * @param {Ext.fx.Anim} anim
156 removeAnim: function(anim) {
157 // this.items.remove(anim);
158 var items = this.items,
161 // Stop the timer if there are no more managed Anims
162 if (task && !items.length) {
163 Ext.TaskManager.stop(task);
170 * Filter function to determine which animations need to be started
172 startingFilter: function(o) {
173 return o.paused === false && o.running === false && o.iterations > 0;
178 * Filter function to determine which animations are still running
180 runningFilter: function(o) {
181 return o.paused === false && o.running === true && o.isAnimator !== true;
186 * Runner function being called each frame
195 // Single timestamp for all animations this interval
196 me.timestamp = new Date();
198 // Start any items not current running
199 items.filterBy(me.startingFilter).each(me.startAnim, me);
201 // Build the new attributes to be applied for all targets in this frame
202 items.filterBy(me.runningFilter).each(me.runAnim, me);
204 // Apply all the pending changes to their targets
205 me.applyPendingAttrs();
210 * Start the individual animation (initialization)
212 startAnim: function(anim) {
213 anim.start(this.timestamp);
218 * Run the individual animation for this frame
220 runAnim: function(anim) {
225 targetId = anim.target.getId(),
226 useCSS3 = me.useCSS3 && anim.target.type == 'element',
227 elapsedTime = me.timestamp - anim.startTime,
230 this.collectTargetData(anim, elapsedTime, useCSS3);
232 // For CSS3 animation, we need to immediately set the first frame's attributes without any transition
233 // to get a good initial state, then add the transition properties and set the final attributes.
235 // Flush the collected attributes, without transition
236 anim.target.setAttr(me.targetData[targetId], true);
238 // Add the end frame data
239 me.targetData[targetId] = [];
240 me.collectTargetData(anim, anim.duration, useCSS3);
242 // Pause the animation so runAnim doesn't keep getting called
245 target = anim.target.target;
246 // We only want to attach an event on the last element in a composite
247 if (anim.target.isComposite) {
248 target = anim.target.target.last();
251 // Listen for the transitionend event
253 o[Ext.supports.CSS3TransitionEnd] = anim.lastFrame;
258 // For JS animation, trigger the lastFrame handler if this is the final frame
259 else if (elapsedTime >= anim.duration) {
260 me.applyPendingAttrs(true);
261 delete me.targetData[targetId];
262 delete me.targetArr[targetId];
268 * Collect target attributes for the given Anim object at the given timestamp
269 * @param {Ext.fx.Anim} anim The Anim instance
270 * @param {Number} timestamp Time after the anim's start time
272 collectTargetData: function(anim, elapsedTime, useCSS3) {
273 var targetId = anim.target.getId(),
274 targetData = this.targetData[targetId],
278 targetData = this.targetData[targetId] = [];
279 this.targetArr[targetId] = anim.target;
283 duration: anim.duration,
284 easing: (useCSS3 && anim.reverse) ? anim.easingFn.reverse().toCSS3() : anim.easing,
287 Ext.apply(data.attrs, anim.runAnim(elapsedTime));
288 targetData.push(data);
293 * Apply all pending attribute changes to their targets
295 applyPendingAttrs: function(isLastFrame) {
296 var targetData = this.targetData,
297 targetArr = this.targetArr,
299 for (targetId in targetData) {
300 if (targetData.hasOwnProperty(targetId)) {
301 targetArr[targetId].setAttr(targetData[targetId], false, isLastFrame);