Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / docs / source / Animator.html
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="../prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../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-Animator'>/**
19 </span> * @class Ext.fx.Animator
20  * Animation instance
21
22 This class is used to run keyframe based animations, which follows the CSS3 based animation structure. 
23 Keyframe animations differ from typical from/to animations in that they offer the ability to specify values 
24 at various points throughout the animation.
25
26 __Using Keyframes__
27 The {@link #keyframes} option is the most important part of specifying an animation when using this 
28 class. A key frame is a point in a particular animation. We represent this as a percentage of the
29 total animation duration. At each key frame, we can specify the target values at that time. Note that
30 you *must* specify the values at 0% and 100%, the start and ending values. There is also a {@link #keyframe}
31 event that fires after each key frame is reached.
32
33 __Example Usage__
34 In the example below, we modify the values of the element at each fifth throughout the animation.
35
36     Ext.create('Ext.fx.Animator', {
37         target: Ext.getBody().createChild({
38             style: {
39                 width: '100px',
40                 height: '100px',
41                 'background-color': 'red'
42             }
43         }),
44         duration: 10000, // 10 seconds
45         keyframes: {
46             0: {
47                 opacity: 1,
48                 backgroundColor: 'FF0000'
49             },
50             20: {
51                 x: 30,
52                 opacity: 0.5    
53             },
54             40: {
55                 x: 130,
56                 backgroundColor: '0000FF'    
57             },
58             60: {
59                 y: 80,
60                 opacity: 0.3    
61             },
62             80: {
63                 width: 200,
64                 y: 200    
65             },
66             100: {
67                 opacity: 1,
68                 backgroundColor: '00FF00'
69             }
70         }
71     });
72
73  * @markdown
74  */
75 Ext.define('Ext.fx.Animator', {
76
77     /* Begin Definitions */
78
79     mixins: {
80         observable: 'Ext.util.Observable'
81     },
82
83     requires: ['Ext.fx.Manager'],
84
85     /* End Definitions */
86
87     isAnimator: true,
88
89 <span id='Ext-fx-Animator-cfg-duration'>    /**
90 </span>     * @cfg {Number} duration
91      * Time in milliseconds for the animation to last. Defaults to 250.
92      */
93     duration: 250,
94
95 <span id='Ext-fx-Animator-cfg-delay'>    /**
96 </span>     * @cfg {Number} delay
97      * Time to delay before starting the animation. Defaults to 0.
98      */
99     delay: 0,
100
101     /* private used to track a delayed starting time */
102     delayStart: 0,
103
104 <span id='Ext-fx-Animator-cfg-dynamic'>    /**
105 </span>     * @cfg {Boolean} dynamic
106      * Currently only for Component Animation: Only set a component's outer element size bypassing layouts.  Set to true to do full layouts for every frame of the animation.  Defaults to false.
107      */
108     dynamic: false,
109
110 <span id='Ext-fx-Animator-cfg-easing'>    /**
111 </span>     * @cfg {String} easing
112
113 This describes how the intermediate values used during a transition will be calculated. It allows for a transition to change
114 speed over its duration. 
115
116 - backIn
117 - backOut
118 - bounceIn
119 - bounceOut
120 - ease
121 - easeIn
122 - easeOut
123 - easeInOut
124 - elasticIn
125 - elasticOut
126 - cubic-bezier(x1, y1, x2, y2)
127
128 Note that cubic-bezier will create a custom easing curve following the CSS3 [transition-timing-function][0]
129 specification.  The four values specify points P1 and P2 of the curve as (x1, y1, x2, y2). All values must
130 be in the range [0, 1] or the definition is invalid.
131
132 [0]: http://www.w3.org/TR/css3-transitions/#transition-timing-function_tag
133
134      * @markdown
135      */
136     easing: 'ease',
137
138 <span id='Ext-fx-Animator-property-running'>    /**
139 </span>     * Flag to determine if the animation has started
140      * @property running
141      * @type boolean
142      */
143     running: false,
144
145 <span id='Ext-fx-Animator-property-paused'>    /**
146 </span>     * Flag to determine if the animation is paused. Only set this to true if you need to
147      * keep the Anim instance around to be unpaused later; otherwise call {@link #end}.
148      * @property paused
149      * @type boolean
150      */
151     paused: false,
152
153 <span id='Ext-fx-Animator-property-damper'>    /**
154 </span>     * @private
155      */
156     damper: 1,
157
158 <span id='Ext-fx-Animator-cfg-iterations'>    /**
159 </span>     * @cfg {Number} iterations
160      * Number of times to execute the animation. Defaults to 1.
161      */
162     iterations: 1,
163
164 <span id='Ext-fx-Animator-property-currentIteration'>    /**
165 </span>     * Current iteration the animation is running.
166      * @property currentIteration
167      * @type int
168      */
169     currentIteration: 0,
170
171 <span id='Ext-fx-Animator-property-keyframeStep'>    /**
172 </span>     * Current keyframe step of the animation.
173      * @property keyframeStep
174      * @type Number
175      */
176     keyframeStep: 0,
177
178 <span id='Ext-fx-Animator-property-animKeyFramesRE'>    /**
179 </span>     * @private
180      */
181     animKeyFramesRE: /^(from|to|\d+%?)$/,
182
183 <span id='Ext-fx-Animator-cfg-target'>    /**
184 </span>     * @cfg {Ext.fx.target} target
185      * The Ext.fx.target to apply the animation to.  If not specified during initialization, this can be passed to the applyAnimator
186      * method to apply the same animation to many targets.
187      */
188
189 <span id='Ext-fx-Animator-cfg-keyframes'>     /**
190 </span>      * @cfg {Object} keyframes
191       * Animation keyframes follow the CSS3 Animation configuration pattern. 'from' is always considered '0%' and 'to'
192       * is considered '100%'.&lt;b&gt;Every keyframe declaration must have a keyframe rule for 0% and 100%, possibly defined using
193       * &quot;from&quot; or &quot;to&quot;&lt;/b&gt;.  A keyframe declaration without these keyframe selectors is invalid and will not be available for
194       * animation.  The keyframe declaration for a keyframe rule consists of properties and values. Properties that are unable to
195       * be animated are ignored in these rules, with the exception of 'easing' which can be changed at each keyframe. For example:
196  &lt;pre&gt;&lt;code&gt;
197 keyframes : {
198     '0%': {
199         left: 100
200     },
201     '40%': {
202         left: 150
203     },
204     '60%': {
205         left: 75
206     },
207     '100%': {
208         left: 100
209     }
210 }
211  &lt;/code&gt;&lt;/pre&gt;
212       */
213     constructor: function(config) {
214         var me = this;
215         config = Ext.apply(me, config || {});
216         me.config = config;
217         me.id = Ext.id(null, 'ext-animator-');
218         me.addEvents(
219 <span id='Ext-fx-Animator-event-beforeanimate'>            /**
220 </span>             * @event beforeanimate
221              * Fires before the animation starts. A handler can return false to cancel the animation.
222              * @param {Ext.fx.Animator} this
223              */
224             'beforeanimate',
225 <span id='Ext-fx-Animator-event-keyframe'>            /**
226 </span>              * @event keyframe
227               * Fires at each keyframe.
228               * @param {Ext.fx.Animator} this
229               * @param {Number} keyframe step number
230               */
231             'keyframe',
232 <span id='Ext-fx-Animator-event-afteranimate'>            /**
233 </span>             * @event afteranimate
234              * Fires when the animation is complete.
235              * @param {Ext.fx.Animator} this
236              * @param {Date} startTime
237              */
238             'afteranimate'
239         );
240         me.mixins.observable.constructor.call(me, config);
241         me.timeline = [];
242         me.createTimeline(me.keyframes);
243         if (me.target) {
244             me.applyAnimator(me.target);
245             Ext.fx.Manager.addAnim(me);
246         }
247     },
248
249 <span id='Ext-fx-Animator-method-sorter'>    /**
250 </span>     * @private
251      */
252     sorter: function (a, b) {
253         return a.pct - b.pct;
254     },
255
256 <span id='Ext-fx-Animator-method-createTimeline'>    /**
257 </span>     * @private
258      * Takes the given keyframe configuration object and converts it into an ordered array with the passed attributes per keyframe
259      * or applying the 'to' configuration to all keyframes.  Also calculates the proper animation duration per keyframe.
260      */
261     createTimeline: function(keyframes) {
262         var me = this,
263             attrs = [],
264             to = me.to || {},
265             duration = me.duration,
266             prevMs, ms, i, ln, pct, anim, nextAnim, attr;
267
268         for (pct in keyframes) {
269             if (keyframes.hasOwnProperty(pct) &amp;&amp; me.animKeyFramesRE.test(pct)) {
270                 attr = {attrs: Ext.apply(keyframes[pct], to)};
271                 // CSS3 spec allow for from/to to be specified.
272                 if (pct == &quot;from&quot;) {
273                     pct = 0;
274                 }
275                 else if (pct == &quot;to&quot;) {
276                     pct = 100;
277                 }
278                 // convert % values into integers
279                 attr.pct = parseInt(pct, 10);
280                 attrs.push(attr);
281             }
282         }
283         // Sort by pct property
284         Ext.Array.sort(attrs, me.sorter);
285         // Only an end
286         //if (attrs[0].pct) {
287         //    attrs.unshift({pct: 0, attrs: element.attrs});
288         //}
289
290         ln = attrs.length;
291         for (i = 0; i &lt; ln; i++) {
292             prevMs = (attrs[i - 1]) ? duration * (attrs[i - 1].pct / 100) : 0;
293             ms = duration * (attrs[i].pct / 100);
294             me.timeline.push({
295                 duration: ms - prevMs,
296                 attrs: attrs[i].attrs
297             });
298         }
299     },
300
301 <span id='Ext-fx-Animator-property-applyAnimator'>    /**
302 </span>     * Applies animation to the Ext.fx.target
303      * @private
304      * @param target
305      * @type string/object
306      */
307     applyAnimator: function(target) {
308         var me = this,
309             anims = [],
310             timeline = me.timeline,
311             reverse = me.reverse,
312             ln = timeline.length,
313             anim, easing, damper, initial, attrs, lastAttrs, i;
314
315         if (me.fireEvent('beforeanimate', me) !== false) {
316             for (i = 0; i &lt; ln; i++) {
317                 anim = timeline[i];
318                 attrs = anim.attrs;
319                 easing = attrs.easing || me.easing;
320                 damper = attrs.damper || me.damper;
321                 delete attrs.easing;
322                 delete attrs.damper;
323                 anim = Ext.create('Ext.fx.Anim', {
324                     target: target,
325                     easing: easing,
326                     damper: damper,
327                     duration: anim.duration,
328                     paused: true,
329                     to: attrs
330                 });
331                 anims.push(anim);
332             }
333             me.animations = anims;
334             me.target = anim.target;
335             for (i = 0; i &lt; ln - 1; i++) {
336                 anim = anims[i];
337                 anim.nextAnim = anims[i + 1];
338                 anim.on('afteranimate', function() {
339                     this.nextAnim.paused = false;
340                 });
341                 anim.on('afteranimate', function() {
342                     this.fireEvent('keyframe', this, ++this.keyframeStep);
343                 }, me);
344             }
345             anims[ln - 1].on('afteranimate', function() {
346                 this.lastFrame();
347             }, me);
348         }
349     },
350
351     /*
352      * @private
353      * Fires beforeanimate and sets the running flag.
354      */
355     start: function(startTime) {
356         var me = this,
357             delay = me.delay,
358             delayStart = me.delayStart,
359             delayDelta;
360         if (delay) {
361             if (!delayStart) {
362                 me.delayStart = startTime;
363                 return;
364             }
365             else {
366                 delayDelta = startTime - delayStart;
367                 if (delayDelta &lt; delay) {
368                     return;
369                 }
370                 else {
371                     // Compensate for frame delay;
372                     startTime = new Date(delayStart.getTime() + delay);
373                 }
374             }
375         }
376         if (me.fireEvent('beforeanimate', me) !== false) {
377             me.startTime = startTime;
378             me.running = true;
379             me.animations[me.keyframeStep].paused = false;
380         }
381     },
382
383     /*
384      * @private
385      * Perform lastFrame cleanup and handle iterations
386      * @returns a hash of the new attributes.
387      */
388     lastFrame: function() {
389         var me = this,
390             iter = me.iterations,
391             iterCount = me.currentIteration;
392
393         iterCount++;
394         if (iterCount &lt; iter) {
395             me.startTime = new Date();
396             me.currentIteration = iterCount;
397             me.keyframeStep = 0;
398             me.applyAnimator(me.target);
399             me.animations[me.keyframeStep].paused = false;
400         }
401         else {
402             me.currentIteration = 0;
403             me.end();
404         }
405     },
406
407     /*
408      * Fire afteranimate event and end the animation. Usually called automatically when the
409      * animation reaches its final frame, but can also be called manually to pre-emptively
410      * stop and destroy the running animation.
411      */
412     end: function() {
413         var me = this;
414         me.fireEvent('afteranimate', me, me.startTime, new Date() - me.startTime);
415     }
416 });</pre>
417 </body>
418 </html>