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>
|