Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / src / util / Animate.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
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.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @class Ext.util.Animate
17  * This animation class is a mixin.
18  * 
19  * Ext.util.Animate provides an API for the creation of animated transitions of properties and styles.  
20  * This class is used as a mixin and currently applied to {@link Ext.Element}, {@link Ext.CompositeElement}, 
21  * {@link Ext.draw.Sprite}, {@link Ext.draw.CompositeSprite}, and {@link Ext.Component}.  Note that Components 
22  * have a limited subset of what attributes can be animated such as top, left, x, y, height, width, and 
23  * opacity (color, paddings, and margins can not be animated).
24  * 
25  * ## Animation Basics
26  * 
27  * All animations require three things - `easing`, `duration`, and `to` (the final end value for each property) 
28  * you wish to animate. Easing and duration are defaulted values specified below.
29  * Easing describes how the intermediate values used during a transition will be calculated. 
30  * {@link Ext.fx.Anim#easing Easing} allows for a transition to change speed over its duration.
31  * You may use the defaults for easing and duration, but you must always set a 
32  * {@link Ext.fx.Anim#to to} property which is the end value for all animations.  
33  * 
34  * Popular element 'to' configurations are:
35  * 
36  *  - opacity
37  *  - x
38  *  - y
39  *  - color
40  *  - height
41  *  - width 
42  * 
43  * Popular sprite 'to' configurations are:
44  * 
45  *  - translation
46  *  - path
47  *  - scale
48  *  - stroke
49  *  - rotation
50  * 
51  * The default duration for animations is 250 (which is a 1/4 of a second).  Duration is denoted in 
52  * milliseconds.  Therefore 1 second is 1000, 1 minute would be 60000, and so on. The default easing curve 
53  * used for all animations is 'ease'.  Popular easing functions are included and can be found in {@link Ext.fx.Anim#easing Easing}.
54  * 
55  * For example, a simple animation to fade out an element with a default easing and duration:
56  * 
57  *     var p1 = Ext.get('myElementId');
58  * 
59  *     p1.animate({
60  *         to: {
61  *             opacity: 0
62  *         }
63  *     });
64  * 
65  * To make this animation fade out in a tenth of a second:
66  * 
67  *     var p1 = Ext.get('myElementId');
68  * 
69  *     p1.animate({
70  *        duration: 100,
71  *         to: {
72  *             opacity: 0
73  *         }
74  *     });
75  * 
76  * ## Animation Queues
77  * 
78  * By default all animations are added to a queue which allows for animation via a chain-style API.
79  * For example, the following code will queue 4 animations which occur sequentially (one right after the other):
80  * 
81  *     p1.animate({
82  *         to: {
83  *             x: 500
84  *         }
85  *     }).animate({
86  *         to: {
87  *             y: 150
88  *         }
89  *     }).animate({
90  *         to: {
91  *             backgroundColor: '#f00'  //red
92  *         }
93  *     }).animate({
94  *         to: {
95  *             opacity: 0
96  *         }
97  *     });
98  * 
99  * You can change this behavior by calling the {@link Ext.util.Animate#syncFx syncFx} method and all 
100  * subsequent animations for the specified target will be run concurrently (at the same time).
101  * 
102  *     p1.syncFx();  //this will make all animations run at the same time
103  * 
104  *     p1.animate({
105  *         to: {
106  *             x: 500
107  *         }
108  *     }).animate({
109  *         to: {
110  *             y: 150
111  *         }
112  *     }).animate({
113  *         to: {
114  *             backgroundColor: '#f00'  //red
115  *         }
116  *     }).animate({
117  *         to: {
118  *             opacity: 0
119  *         }
120  *     });
121  * 
122  * This works the same as:
123  * 
124  *     p1.animate({
125  *         to: {
126  *             x: 500,
127  *             y: 150,
128  *             backgroundColor: '#f00'  //red
129  *             opacity: 0
130  *         }
131  *     });
132  * 
133  * The {@link Ext.util.Animate#stopAnimation stopAnimation} method can be used to stop any 
134  * currently running animations and clear any queued animations. 
135  * 
136  * ## Animation Keyframes
137  *
138  * You can also set up complex animations with {@link Ext.fx.Anim#keyframes keyframes} which follow the 
139  * CSS3 Animation configuration pattern. Note rotation, translation, and scaling can only be done for sprites. 
140  * The previous example can be written with the following syntax:
141  * 
142  *     p1.animate({
143  *         duration: 1000,  //one second total
144  *         keyframes: {
145  *             25: {     //from 0 to 250ms (25%)
146  *                 x: 0
147  *             },
148  *             50: {   //from 250ms to 500ms (50%)
149  *                 y: 0
150  *             },
151  *             75: {  //from 500ms to 750ms (75%)
152  *                 backgroundColor: '#f00'  //red
153  *             },
154  *             100: {  //from 750ms to 1sec
155  *                 opacity: 0
156  *             }
157  *         }
158  *     });
159  * 
160  * ## Animation Events
161  * 
162  * Each animation you create has events for {@link Ext.fx.Anim#beforeanimate beforeanimate}, 
163  * {@link Ext.fx.Anim#afteranimate afteranimate}, and {@link Ext.fx.Anim#lastframe lastframe}.  
164  * Keyframed animations adds an additional {@link Ext.fx.Animator#keyframe keyframe} event which 
165  * fires for each keyframe in your animation.
166  * 
167  * All animations support the {@link Ext.util.Observable#listeners listeners} configuration to attact functions to these events.
168  *    
169  *     startAnimate: function() {
170  *         var p1 = Ext.get('myElementId');
171  *         p1.animate({
172  *            duration: 100,
173  *             to: {
174  *                 opacity: 0
175  *             },
176  *             listeners: {
177  *                 beforeanimate:  function() {
178  *                     // Execute my custom method before the animation
179  *                     this.myBeforeAnimateFn();
180  *                 },
181  *                 afteranimate: function() {
182  *                     // Execute my custom method after the animation
183  *                     this.myAfterAnimateFn();
184  *                 },
185  *                 scope: this
186  *         });
187  *     },
188  *     myBeforeAnimateFn: function() {
189  *       // My custom logic
190  *     },
191  *     myAfterAnimateFn: function() {
192  *       // My custom logic
193  *     }
194  * 
195  * Due to the fact that animations run asynchronously, you can determine if an animation is currently 
196  * running on any target by using the {@link Ext.util.Animate#getActiveAnimation getActiveAnimation} 
197  * method.  This method will return false if there are no active animations or return the currently 
198  * running {@link Ext.fx.Anim} instance.
199  * 
200  * In this example, we're going to wait for the current animation to finish, then stop any other 
201  * queued animations before we fade our element's opacity to 0:
202  * 
203  *     var curAnim = p1.getActiveAnimation();
204  *     if (curAnim) {
205  *         curAnim.on('afteranimate', function() {
206  *             p1.stopAnimation();
207  *             p1.animate({
208  *                 to: {
209  *                     opacity: 0
210  *                 }
211  *             });
212  *         });
213  *     }
214  * 
215  * @docauthor Jamie Avins <jamie@sencha.com>
216  */
217 Ext.define('Ext.util.Animate', {
218
219     uses: ['Ext.fx.Manager', 'Ext.fx.Anim'],
220
221     /**
222      * <p>Perform custom animation on this object.<p>
223      * <p>This method is applicable to both the {@link Ext.Component Component} class and the {@link Ext.Element Element} class.
224      * It performs animated transitions of certain properties of this object over a specified timeline.</p>
225      * <p>The sole parameter is an object which specifies start property values, end property values, and properties which
226      * describe the timeline. Of the properties listed below, only <b><code>to</code></b> is mandatory.</p>
227      * <p>Properties include<ul>
228      * <li><code>from</code> <div class="sub-desc">An object which specifies start values for the properties being animated.
229      * If not supplied, properties are animated from current settings. The actual properties which may be animated depend upon
230      * ths object being animated. See the sections below on Element and Component animation.<div></li>
231      * <li><code>to</code> <div class="sub-desc">An object which specifies end values for the properties being animated.</div></li>
232      * <li><code>duration</code><div class="sub-desc">The duration <b>in milliseconds</b> for which the animation will run.</div></li>
233      * <li><code>easing</code> <div class="sub-desc">A string value describing an easing type to modify the rate of change from the default linear to non-linear. Values may be one of:<code><ul>
234      * <li>ease</li>
235      * <li>easeIn</li>
236      * <li>easeOut</li>
237      * <li>easeInOut</li>
238      * <li>backIn</li>
239      * <li>backOut</li>
240      * <li>elasticIn</li>
241      * <li>elasticOut</li>
242      * <li>bounceIn</li>
243      * <li>bounceOut</li>
244      * </ul></code></div></li>
245      * <li><code>keyframes</code> <div class="sub-desc">This is an object which describes the state of animated properties at certain points along the timeline.
246      * it is an object containing properties who's names are the percentage along the timeline being described and who's values specify the animation state at that point.</div></li>
247      * <li><code>listeners</code> <div class="sub-desc">This is a standard {@link Ext.util.Observable#listeners listeners} configuration object which may be used
248      * to inject behaviour at either the <code>beforeanimate</code> event or the <code>afteranimate</code> event.</div></li>
249      * </ul></p>
250      * <h3>Animating an {@link Ext.Element Element}</h3>
251      * When animating an Element, the following properties may be specified in <code>from</code>, <code>to</code>, and <code>keyframe</code> objects:<ul>
252      * <li><code>x</code> <div class="sub-desc">The page X position in pixels.</div></li>
253      * <li><code>y</code> <div class="sub-desc">The page Y position in pixels</div></li>
254      * <li><code>left</code> <div class="sub-desc">The element's CSS <code>left</code> value. Units must be supplied.</div></li>
255      * <li><code>top</code> <div class="sub-desc">The element's CSS <code>top</code> value. Units must be supplied.</div></li>
256      * <li><code>width</code> <div class="sub-desc">The element's CSS <code>width</code> value. Units must be supplied.</div></li>
257      * <li><code>height</code> <div class="sub-desc">The element's CSS <code>height</code> value. Units must be supplied.</div></li>
258      * <li><code>scrollLeft</code> <div class="sub-desc">The element's <code>scrollLeft</code> value.</div></li>
259      * <li><code>scrollTop</code> <div class="sub-desc">The element's <code>scrollLeft</code> value.</div></li>
260      * <li><code>opacity</code> <div class="sub-desc">The element's <code>opacity</code> value. This must be a value between <code>0</code> and <code>1</code>.</div></li>
261      * </ul>
262      * <p><b>Be aware than animating an Element which is being used by an Ext Component without in some way informing the Component about the changed element state
263      * will result in incorrect Component behaviour. This is because the Component will be using the old state of the element. To avoid this problem, it is now possible to
264      * directly animate certain properties of Components.</b></p>
265      * <h3>Animating a {@link Ext.Component Component}</h3>
266      * When animating an Element, the following properties may be specified in <code>from</code>, <code>to</code>, and <code>keyframe</code> objects:<ul>
267      * <li><code>x</code> <div class="sub-desc">The Component's page X position in pixels.</div></li>
268      * <li><code>y</code> <div class="sub-desc">The Component's page Y position in pixels</div></li>
269      * <li><code>left</code> <div class="sub-desc">The Component's <code>left</code> value in pixels.</div></li>
270      * <li><code>top</code> <div class="sub-desc">The Component's <code>top</code> value in pixels.</div></li>
271      * <li><code>width</code> <div class="sub-desc">The Component's <code>width</code> value in pixels.</div></li>
272      * <li><code>width</code> <div class="sub-desc">The Component's <code>width</code> value in pixels.</div></li>
273      * <li><code>dynamic</code> <div class="sub-desc">Specify as true to update the Component's layout (if it is a Container) at every frame
274      * of the animation. <i>Use sparingly as laying out on every intermediate size change is an expensive operation</i>.</div></li>
275      * </ul>
276      * <p>For example, to animate a Window to a new size, ensuring that its internal layout, and any shadow is correct:</p>
277      * <pre><code>
278 myWindow = Ext.create('Ext.window.Window', {
279     title: 'Test Component animation',
280     width: 500,
281     height: 300,
282     layout: {
283         type: 'hbox',
284         align: 'stretch'
285     },
286     items: [{
287         title: 'Left: 33%',
288         margins: '5 0 5 5',
289         flex: 1
290     }, {
291         title: 'Left: 66%',
292         margins: '5 5 5 5',
293         flex: 2
294     }]
295 });
296 myWindow.show();
297 myWindow.header.el.on('click', function() {
298     myWindow.animate({
299         to: {
300             width: (myWindow.getWidth() == 500) ? 700 : 500,
301             height: (myWindow.getHeight() == 300) ? 400 : 300,
302         }
303     });
304 });
305 </code></pre>
306      * <p>For performance reasons, by default, the internal layout is only updated when the Window reaches its final <code>"to"</code> size. If dynamic updating of the Window's child
307      * Components is required, then configure the animation with <code>dynamic: true</code> and the two child items will maintain their proportions during the animation.</p>
308      * @param {Object} config An object containing properties which describe the animation's start and end states, and the timeline of the animation.
309      * @return {Object} this
310      */
311     animate: function(animObj) {
312         var me = this;
313         if (Ext.fx.Manager.hasFxBlock(me.id)) {
314             return me;
315         }
316         Ext.fx.Manager.queueFx(Ext.create('Ext.fx.Anim', me.anim(animObj)));
317         return this;
318     },
319
320     // @private - process the passed fx configuration.
321     anim: function(config) {
322         if (!Ext.isObject(config)) {
323             return (config) ? {} : false;
324         }
325
326         var me = this;
327
328         if (config.stopAnimation) {
329             me.stopAnimation();
330         }
331
332         Ext.applyIf(config, Ext.fx.Manager.getFxDefaults(me.id));
333
334         return Ext.apply({
335             target: me,
336             paused: true
337         }, config);
338     },
339
340     /**
341      * @deprecated 4.0 Replaced by {@link #stopAnimation}
342      * Stops any running effects and clears this object's internal effects queue if it contains
343      * any additional effects that haven't started yet.
344      * @return {Ext.Element} The Element
345      * @method
346      */
347     stopFx: Ext.Function.alias(Ext.util.Animate, 'stopAnimation'),
348
349     /**
350      * Stops any running effects and clears this object's internal effects queue if it contains
351      * any additional effects that haven't started yet.
352      * @return {Ext.Element} The Element
353      */
354     stopAnimation: function() {
355         Ext.fx.Manager.stopAnimation(this.id);
356         return this;
357     },
358
359     /**
360      * Ensures that all effects queued after syncFx is called on this object are
361      * run concurrently.  This is the opposite of {@link #sequenceFx}.
362      * @return {Object} this
363      */
364     syncFx: function() {
365         Ext.fx.Manager.setFxDefaults(this.id, {
366             concurrent: true
367         });
368         return this;
369     },
370
371     /**
372      * Ensures that all effects queued after sequenceFx is called on this object are
373      * run in sequence.  This is the opposite of {@link #syncFx}.
374      * @return {Object} this
375      */
376     sequenceFx: function() {
377         Ext.fx.Manager.setFxDefaults(this.id, {
378             concurrent: false
379         });
380         return this;
381     },
382
383     /**
384      * @deprecated 4.0 Replaced by {@link #getActiveAnimation}
385      * @alias Ext.util.Animate#getActiveAnimation
386      * @method
387      */
388     hasActiveFx: Ext.Function.alias(Ext.util.Animate, 'getActiveAnimation'),
389
390     /**
391      * Returns the current animation if this object has any effects actively running or queued, else returns false.
392      * @return {Ext.fx.Anim/Boolean} Anim if element has active effects, else false
393      */
394     getActiveAnimation: function() {
395         return Ext.fx.Manager.getActiveAnimation(this.id);
396     }
397 }, function(){
398     // Apply Animate mixin manually until Element is defined in the proper 4.x way
399     Ext.applyIf(Ext.Element.prototype, this.prototype);
400     // We need to call this again so the animation methods get copied over to CE
401     Ext.CompositeElementLite.importElementMethods();
402 });