3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * @class Ext.fx.Manager
17 * Animation Manager which keeps track of all current animations and manages them on a frame by frame basis.
22 Ext.define('Ext.fx.Manager', {
24 /* Begin Definitions */
28 requires: ['Ext.util.MixedCollection',
29 'Ext.fx.target.Element',
30 'Ext.fx.target.CompositeElement',
31 'Ext.fx.target.Sprite',
32 'Ext.fx.target.CompositeSprite',
33 'Ext.fx.target.Component'],
41 constructor: function() {
42 this.items = Ext.create('Ext.util.MixedCollection');
43 this.mixins.queue.constructor.call(this);
45 // this.requestAnimFrame = (function() {
46 // var raf = window.requestAnimationFrame ||
47 // window.webkitRequestAnimationFrame ||
48 // window.mozRequestAnimationFrame ||
49 // window.oRequestAnimationFrame ||
50 // window.msRequestAnimationFrame;
52 // return function(callback, element) {
57 // return function(callback, element) {
58 // window.setTimeout(callback, Ext.fx.Manager.interval);
65 * @cfg {Number} interval Default interval in miliseconds to calculate each frame. Defaults to 16ms (~60fps)
70 * @cfg {Boolean} forceJS Turn off to not use CSS3 transitions when they are available
74 // @private Target factory
75 createTarget: function(target) {
77 useCSS3 = !me.forceJS && Ext.supports.Transitions,
83 if (Ext.isString(target)) {
84 target = Ext.get(target);
87 if (target && target.tagName) {
88 target = Ext.get(target);
89 targetObj = Ext.create('Ext.fx.target.' + 'Element' + (useCSS3 ? 'CSS' : ''), target);
90 me.targets.add(targetObj);
93 if (Ext.isObject(target)) {
96 targetObj = Ext.create('Ext.fx.target.' + 'Element' + (useCSS3 ? 'CSS' : ''), target);
99 else if (target.isComposite) {
100 targetObj = Ext.create('Ext.fx.target.' + 'CompositeElement' + (useCSS3 ? 'CSS' : ''), target);
103 else if (target.isSprite) {
104 targetObj = Ext.create('Ext.fx.target.Sprite', target);
106 // Draw Sprite Composite
107 else if (target.isCompositeSprite) {
108 targetObj = Ext.create('Ext.fx.target.CompositeSprite', target);
111 else if (target.isComponent) {
112 targetObj = Ext.create('Ext.fx.target.Component', target);
114 else if (target.isAnimTarget) {
120 me.targets.add(targetObj);
129 * Add an Anim to the manager. This is done automatically when an Anim instance is created.
130 * @param {Ext.fx.Anim} anim
132 addAnim: function(anim) {
133 var items = this.items,
138 // if (items.length) {
141 // me.requestAnimFrame(cb);
150 // Start the timer if not already running
151 if (!task && items.length) {
154 interval: this.interval,
157 Ext.TaskManager.start(task);
160 // //Start the timer if not already running
161 // if (!me.task && items.length) {
162 // me.requestAnimFrame(cb);
167 * Remove an Anim from the manager. This is done automatically when an Anim ends.
168 * @param {Ext.fx.Anim} anim
170 removeAnim: function(anim) {
171 // this.items.remove(anim);
172 var items = this.items,
175 // Stop the timer if there are no more managed Anims
176 if (task && !items.length) {
177 Ext.TaskManager.stop(task);
184 * Filter function to determine which animations need to be started
186 startingFilter: function(o) {
187 return o.paused === false && o.running === false && o.iterations > 0;
192 * Filter function to determine which animations are still running
194 runningFilter: function(o) {
195 return o.paused === false && o.running === true && o.isAnimator !== true;
200 * Runner function being called each frame
209 // Single timestamp for all animations this interval
210 me.timestamp = new Date();
212 // Start any items not current running
213 items.filterBy(me.startingFilter).each(me.startAnim, me);
215 // Build the new attributes to be applied for all targets in this frame
216 items.filterBy(me.runningFilter).each(me.runAnim, me);
218 // Apply all the pending changes to their targets
219 me.applyPendingAttrs();
224 * Start the individual animation (initialization)
226 startAnim: function(anim) {
227 anim.start(this.timestamp);
232 * Run the individual animation for this frame
234 runAnim: function(anim) {
239 targetId = anim.target.getId(),
240 useCSS3 = me.useCSS3 && anim.target.type == 'element',
241 elapsedTime = me.timestamp - anim.startTime,
244 this.collectTargetData(anim, elapsedTime, useCSS3);
246 // For CSS3 animation, we need to immediately set the first frame's attributes without any transition
247 // to get a good initial state, then add the transition properties and set the final attributes.
249 // Flush the collected attributes, without transition
250 anim.target.setAttr(me.targetData[targetId], true);
252 // Add the end frame data
253 me.targetData[targetId] = [];
254 me.collectTargetData(anim, anim.duration, useCSS3);
256 // Pause the animation so runAnim doesn't keep getting called
259 target = anim.target.target;
260 // We only want to attach an event on the last element in a composite
261 if (anim.target.isComposite) {
262 target = anim.target.target.last();
265 // Listen for the transitionend event
267 o[Ext.supports.CSS3TransitionEnd] = anim.lastFrame;
272 // For JS animation, trigger the lastFrame handler if this is the final frame
273 else if (elapsedTime >= anim.duration) {
274 me.applyPendingAttrs(true);
275 delete me.targetData[targetId];
276 delete me.targetArr[targetId];
282 * Collect target attributes for the given Anim object at the given timestamp
283 * @param {Ext.fx.Anim} anim The Anim instance
284 * @param {Number} timestamp Time after the anim's start time
286 collectTargetData: function(anim, elapsedTime, useCSS3) {
287 var targetId = anim.target.getId(),
288 targetData = this.targetData[targetId],
292 targetData = this.targetData[targetId] = [];
293 this.targetArr[targetId] = anim.target;
297 duration: anim.duration,
298 easing: (useCSS3 && anim.reverse) ? anim.easingFn.reverse().toCSS3() : anim.easing,
301 Ext.apply(data.attrs, anim.runAnim(elapsedTime));
302 targetData.push(data);
307 * Apply all pending attribute changes to their targets
309 applyPendingAttrs: function(isLastFrame) {
310 var targetData = this.targetData,
311 targetArr = this.targetArr,
313 for (targetId in targetData) {
314 if (targetData.hasOwnProperty(targetId)) {
315 targetArr[targetId].setAttr(targetData[targetId], false, isLastFrame);