| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373 | <!DOCTYPE html><html><head>  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  <title>The source code</title>  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>  <style type="text/css">    .highlight { display: block; background-color: #ddd; }  </style>  <script type="text/javascript">    function highlight() {      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";    }  </script></head><body onload="prettyPrint(); highlight();">  <pre class="prettyprint lang-js"><span id='Ext-fx-Manager'>/**</span> * @class Ext.fx.Manager * Animation Manager which keeps track of all current animations and manages them on a frame by frame basis. * @private * @singleton */Ext.define('Ext.fx.Manager', {    /* Begin Definitions */    singleton: true,    requires: ['Ext.util.MixedCollection',               'Ext.fx.target.Element',               'Ext.fx.target.ElementCSS',               'Ext.fx.target.CompositeElement',               'Ext.fx.target.CompositeElementCSS',               'Ext.fx.target.Sprite',               'Ext.fx.target.CompositeSprite',               'Ext.fx.target.Component'],    mixins: {        queue: 'Ext.fx.Queue'    },    /* End Definitions */    constructor: function() {        this.items = new Ext.util.MixedCollection();        this.mixins.queue.constructor.call(this);        // this.requestAnimFrame = (function() {        //     var raf = window.requestAnimationFrame ||        //               window.webkitRequestAnimationFrame ||        //               window.mozRequestAnimationFrame ||        //               window.oRequestAnimationFrame ||        //               window.msRequestAnimationFrame;        //     if (raf) {        //         return function(callback, element) {        //             raf(callback);        //         };        //     }        //     else {        //         return function(callback, element) {        //             window.setTimeout(callback, Ext.fx.Manager.interval);        //         };        //     }        // })();    },<span id='Ext-fx-Manager-cfg-interval'>    /**</span>     * @cfg {Number} interval Default interval in miliseconds to calculate each frame.  Defaults to 16ms (~60fps)     */    interval: 16,<span id='Ext-fx-Manager-cfg-forceJS'>    /**</span>     * @cfg {Boolean} forceJS Force the use of JavaScript-based animation instead of CSS3 animation, even when CSS3     * animation is supported by the browser. This defaults to true currently, as CSS3 animation support is still     * considered experimental at this time, and if used should be thouroughly tested across all targeted browsers.     * @protected     */    forceJS: true,    // @private Target factory    createTarget: function(target) {        var me = this,            useCSS3 = !me.forceJS && Ext.supports.Transitions,            targetObj;        me.useCSS3 = useCSS3;        if (target) {            // dom element, string or fly            if (target.tagName || Ext.isString(target) || target.isFly) {                target = Ext.get(target);                targetObj = new Ext.fx.target['Element' + (useCSS3 ? 'CSS' : '')](target);            }            // Element            else if (target.dom) {                targetObj = new Ext.fx.target['Element' + (useCSS3 ? 'CSS' : '')](target);            }            // Element Composite            else if (target.isComposite) {                targetObj = new Ext.fx.target['CompositeElement' + (useCSS3 ? 'CSS' : '')](target);            }            // Draw Sprite            else if (target.isSprite) {                targetObj = new Ext.fx.target.Sprite(target);            }            // Draw Sprite Composite            else if (target.isCompositeSprite) {                targetObj = new Ext.fx.target.CompositeSprite(target);            }            // Component            else if (target.isComponent) {                targetObj = new Ext.fx.target.Component(target);            }            else if (target.isAnimTarget) {                return target;            }            else {                return null;            }            me.targets.add(targetObj);            return targetObj;        }        else {            return null;        }    },<span id='Ext-fx-Manager-method-addAnim'>    /**</span>     * Add an Anim to the manager. This is done automatically when an Anim instance is created.     * @param {Ext.fx.Anim} anim     */    addAnim: function(anim) {        var items = this.items,            task = this.task;        // Make sure we use the anim's id, not the anim target's id here. The anim id will be unique on        // each call to addAnim. `anim.target` is the DOM element being targeted, and since multiple animations        // can target a single DOM node concurrently, the target id cannot be assumned to be unique.        items.add(anim.id, anim);        //Ext.log('+     added anim ', anim.id, ', target: ', anim.target.getId(), ', duration: ', anim.duration);        // Start the timer if not already running        if (!task && items.length) {            task = this.task = {                run: this.runner,                interval: this.interval,                scope: this            };            //Ext.log('--->> Starting task');            Ext.TaskManager.start(task);        }    },<span id='Ext-fx-Manager-method-removeAnim'>    /**</span>     * Remove an Anim from the manager. This is done automatically when an Anim ends.     * @param {Ext.fx.Anim} anim     */    removeAnim: function(anim) {        var me = this,            items = me.items,            task = me.task;                        items.removeAtKey(anim.id);        //Ext.log('    X removed anim ', anim.id, ', target: ', anim.target.getId(), ', frames: ', anim.frameCount, ', item count: ', items.length);                // Stop the timer if there are no more managed Anims        if (task && !items.length) {            //Ext.log('[]--- Stopping task');            Ext.TaskManager.stop(task);            delete me.task;        }    },<span id='Ext-fx-Manager-method-runner'>    /**</span>     * @private     * Runner function being called each frame     */    runner: function() {        var me = this,            items = me.items.getRange(),            i = 0,            len = items.length,            anim;        //Ext.log('      executing anim runner task with ', len, ' items');        me.targetArr = {};        // Single timestamp for all animations this interval        me.timestamp = new Date();                // Loop to start any new animations first before looping to        // execute running animations (which will also include all animations        // started in this loop). This is a subtle difference from simply        // iterating in one loop and starting then running each animation,        // but separating the loops is necessary to ensure that all new animations        // actually kick off prior to existing ones regardless of array order.        // Otherwise in edge cases when there is excess latency in overall        // performance, allowing existing animations to run before new ones can        // lead to dropped frames and subtle race conditions when they are        // interdependent, which is often the case with certain Element fx.        for (; i < len; i++) {            anim = items[i];                        if (anim.isReady()) {                //Ext.log('      starting anim ', anim.id, ', target: ', anim.target.id);                me.startAnim(anim);            }        }                for (i = 0; i < len; i++) {            anim = items[i];                        if (anim.isRunning()) {                //Ext.log('      running anim ', anim.target.id);                me.runAnim(anim);            //<debug>            } else if (!me.useCSS3) {                // When using CSS3 transitions the animations get paused since they are not                // needed once the transition is handed over to the browser, so we can                // ignore this case. However if we are doing JS animations and something is                // paused here it's possibly unintentional.                //Ext.log(' (i)  anim ', anim.id, ' is active but not running...');            //</debug>            }        }        // Apply all the pending changes to their targets        me.applyPendingAttrs();    },<span id='Ext-fx-Manager-method-startAnim'>    /**</span>     * @private     * Start the individual animation (initialization)     */    startAnim: function(anim) {        anim.start(this.timestamp);    },<span id='Ext-fx-Manager-method-runAnim'>    /**</span>     * @private     * Run the individual animation for this frame     */    runAnim: function(anim) {        if (!anim) {            return;        }        var me = this,            targetId = anim.target.getId(),            useCSS3 = me.useCSS3 && anim.target.type == 'element',            elapsedTime = me.timestamp - anim.startTime,            lastFrame = (elapsedTime >= anim.duration),            target, o;        target = this.collectTargetData(anim, elapsedTime, useCSS3, lastFrame);                // For CSS3 animation, we need to immediately set the first frame's attributes without any transition        // to get a good initial state, then add the transition properties and set the final attributes.        if (useCSS3) {            //Ext.log(' (i)  using CSS3 transitions');                        // Flush the collected attributes, without transition            anim.target.setAttr(target.anims[anim.id].attributes, true);            // Add the end frame data            me.collectTargetData(anim, anim.duration, useCSS3, lastFrame);            // Pause the animation so runAnim doesn't keep getting called            anim.paused = true;            target = anim.target.target;            // We only want to attach an event on the last element in a composite            if (anim.target.isComposite) {                target = anim.target.target.last();            }            // Listen for the transitionend event            o = {};            o[Ext.supports.CSS3TransitionEnd] = anim.lastFrame;            o.scope = anim;            o.single = true;            target.on(o);        }    },<span id='Ext-fx-Manager-method-collectTargetData'>    /**</span>     * @private     * Collect target attributes for the given Anim object at the given timestamp     * @param {Ext.fx.Anim} anim The Anim instance     * @param {Number} timestamp Time after the anim's start time     * @param {Boolean} [useCSS3=false] True if using CSS3-based animation, else false     * @param {Boolean} [isLastFrame=false] True if this is the last frame of animation to be run, else false     * @return {Object} The animation target wrapper object containing the passed animation along with the     * new attributes to set on the target's element in the next animation frame.     */    collectTargetData: function(anim, elapsedTime, useCSS3, isLastFrame) {        var targetId = anim.target.getId(),            target = this.targetArr[targetId];                if (!target) {            // Create a thin wrapper around the target so that we can create a link between the            // target element and its associated animations. This is important later when applying            // attributes to the target so that each animation can be independently run with its own            // duration and stopped at any point without affecting other animations for the same target.            target = this.targetArr[targetId] = {                id: targetId,                el: anim.target,                anims: {}            };        }        // This is a wrapper for the animation so that we can also save state along with it,        // including the current elapsed time and lastFrame status. Even though this method only        // adds a single anim object per call, each target element could have multiple animations        // associated with it, which is why the anim is added to the target's `anims` hash by id.        target.anims[anim.id] = {            id: anim.id,            anim: anim,            elapsed: elapsedTime,            isLastFrame: isLastFrame,            // This is the object that gets applied to the target element below in applyPendingAttrs():            attributes: [{                duration: anim.duration,                easing: (useCSS3 && anim.reverse) ? anim.easingFn.reverse().toCSS3() : anim.easing,                // This is where the magic happens. The anim calculates what its new attributes should                // be based on the current frame and returns those as a hash of values.                attrs: anim.runAnim(elapsedTime)            }]        };                return target;    },    <span id='Ext-fx-Manager-method-applyPendingAttrs'>    /**</span>     * @private     * Apply all pending attribute changes to their targets     */    applyPendingAttrs: function() {        var targetArr = this.targetArr,            target, targetId, animWrap, anim, animId;                // Loop through each target        for (targetId in targetArr) {            if (targetArr.hasOwnProperty(targetId)) {                target = targetArr[targetId];                                // Each target could have multiple associated animations, so iterate those                for (animId in target.anims) {                    if (target.anims.hasOwnProperty(animId)) {                        animWrap = target.anims[animId];                        anim = animWrap.anim;                                                // If the animation has valid attributes, set them on the target                        if (animWrap.attributes && anim.isRunning()) {                            //Ext.log('  >   applying attributes for anim ', animWrap.id, ', target: ', target.id, ', elapsed: ', animWrap.elapsed);                            target.el.setAttr(animWrap.attributes, false, animWrap.isLastFrame);                                                        // If this particular anim is at the last frame end it                            if (animWrap.isLastFrame) {                                //Ext.log('      running last frame for ', animWrap.id, ', target: ', targetId);                                anim.lastFrame();                            }                        }                    }                }            }        }    }});</pre></body></html>
 |