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.Animator
18 * This class is used to run keyframe based animations, which follows the CSS3 based animation structure.
19 * Keyframe animations differ from typical from/to animations in that they offer the ability to specify values
20 * at various points throughout the animation.
24 * The {@link #keyframes} option is the most important part of specifying an animation when using this
25 * class. A key frame is a point in a particular animation. We represent this as a percentage of the
26 * total animation duration. At each key frame, we can specify the target values at that time. Note that
27 * you *must* specify the values at 0% and 100%, the start and ending values. There is also a {@link #keyframe}
28 * event that fires after each key frame is reached.
32 * In the example below, we modify the values of the element at each fifth throughout the animation.
35 * Ext.create('Ext.fx.Animator', {
36 * target: Ext.getBody().createChild({
40 * 'background-color': 'red'
43 * duration: 10000, // 10 seconds
47 * backgroundColor: 'FF0000'
55 * backgroundColor: '0000FF'
67 * backgroundColor: '00FF00'
72 Ext.define('Ext.fx.Animator', {
74 /* Begin Definitions */
77 observable: 'Ext.util.Observable'
80 requires: ['Ext.fx.Manager'],
87 * @cfg {Number} duration
88 * Time in milliseconds for the animation to last. Defaults to 250.
94 * Time to delay before starting the animation. Defaults to 0.
98 /* private used to track a delayed starting time */
102 * @cfg {Boolean} dynamic
103 * 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.
108 * @cfg {String} easing
110 * This describes how the intermediate values used during a transition will be calculated. It allows for a transition to change
111 * speed over its duration.
123 * - cubic-bezier(x1, y1, x2, y2)
125 * Note that cubic-bezier will create a custom easing curve following the CSS3 [transition-timing-function][0]
126 * specification. The four values specify points P1 and P2 of the curve as (x1, y1, x2, y2). All values must
127 * be in the range [0, 1] or the definition is invalid.
129 * [0]: http://www.w3.org/TR/css3-transitions/#transition-timing-function_tag
134 * Flag to determine if the animation has started
141 * Flag to determine if the animation is paused. Only set this to true if you need to
142 * keep the Anim instance around to be unpaused later; otherwise call {@link #end}.
154 * @cfg {Number} iterations
155 * Number of times to execute the animation. Defaults to 1.
160 * Current iteration the animation is running.
161 * @property currentIteration
167 * Current keyframe step of the animation.
168 * @property keyframeStep
176 animKeyFramesRE: /^(from|to|\d+%?)$/,
179 * @cfg {Ext.fx.target.Target} target
180 * The Ext.fx.target to apply the animation to. If not specified during initialization, this can be passed to the applyAnimator
181 * method to apply the same animation to many targets.
185 * @cfg {Object} keyframes
186 * Animation keyframes follow the CSS3 Animation configuration pattern. 'from' is always considered '0%' and 'to'
187 * is considered '100%'.<b>Every keyframe declaration must have a keyframe rule for 0% and 100%, possibly defined using
188 * "from" or "to"</b>. A keyframe declaration without these keyframe selectors is invalid and will not be available for
189 * animation. The keyframe declaration for a keyframe rule consists of properties and values. Properties that are unable to
190 * be animated are ignored in these rules, with the exception of 'easing' which can be changed at each keyframe. For example:
208 constructor: function(config) {
210 config = Ext.apply(me, config || {});
212 me.id = Ext.id(null, 'ext-animator-');
215 * @event beforeanimate
216 * Fires before the animation starts. A handler can return false to cancel the animation.
217 * @param {Ext.fx.Animator} this
222 * Fires at each keyframe.
223 * @param {Ext.fx.Animator} this
224 * @param {Number} keyframe step number
228 * @event afteranimate
229 * Fires when the animation is complete.
230 * @param {Ext.fx.Animator} this
231 * @param {Date} startTime
235 me.mixins.observable.constructor.call(me, config);
237 me.createTimeline(me.keyframes);
239 me.applyAnimator(me.target);
240 Ext.fx.Manager.addAnim(me);
247 sorter: function (a, b) {
248 return a.pct - b.pct;
253 * Takes the given keyframe configuration object and converts it into an ordered array with the passed attributes per keyframe
254 * or applying the 'to' configuration to all keyframes. Also calculates the proper animation duration per keyframe.
256 createTimeline: function(keyframes) {
260 duration = me.duration,
261 prevMs, ms, i, ln, pct, anim, nextAnim, attr;
263 for (pct in keyframes) {
264 if (keyframes.hasOwnProperty(pct) && me.animKeyFramesRE.test(pct)) {
265 attr = {attrs: Ext.apply(keyframes[pct], to)};
266 // CSS3 spec allow for from/to to be specified.
270 else if (pct == "to") {
273 // convert % values into integers
274 attr.pct = parseInt(pct, 10);
278 // Sort by pct property
279 Ext.Array.sort(attrs, me.sorter);
281 //if (attrs[0].pct) {
282 // attrs.unshift({pct: 0, attrs: element.attrs});
286 for (i = 0; i < ln; i++) {
287 prevMs = (attrs[i - 1]) ? duration * (attrs[i - 1].pct / 100) : 0;
288 ms = duration * (attrs[i].pct / 100);
290 duration: ms - prevMs,
291 attrs: attrs[i].attrs
297 * Applies animation to the Ext.fx.target
300 * @type String/Object
302 applyAnimator: function(target) {
305 timeline = me.timeline,
306 reverse = me.reverse,
307 ln = timeline.length,
308 anim, easing, damper, initial, attrs, lastAttrs, i;
310 if (me.fireEvent('beforeanimate', me) !== false) {
311 for (i = 0; i < ln; i++) {
314 easing = attrs.easing || me.easing;
315 damper = attrs.damper || me.damper;
318 anim = Ext.create('Ext.fx.Anim', {
322 duration: anim.duration,
328 me.animations = anims;
329 me.target = anim.target;
330 for (i = 0; i < ln - 1; i++) {
332 anim.nextAnim = anims[i + 1];
333 anim.on('afteranimate', function() {
334 this.nextAnim.paused = false;
336 anim.on('afteranimate', function() {
337 this.fireEvent('keyframe', this, ++this.keyframeStep);
340 anims[ln - 1].on('afteranimate', function() {
348 * Fires beforeanimate and sets the running flag.
350 start: function(startTime) {
353 delayStart = me.delayStart,
357 me.delayStart = startTime;
361 delayDelta = startTime - delayStart;
362 if (delayDelta < delay) {
366 // Compensate for frame delay;
367 startTime = new Date(delayStart.getTime() + delay);
371 if (me.fireEvent('beforeanimate', me) !== false) {
372 me.startTime = startTime;
374 me.animations[me.keyframeStep].paused = false;
380 * Perform lastFrame cleanup and handle iterations
381 * @returns a hash of the new attributes.
383 lastFrame: function() {
385 iter = me.iterations,
386 iterCount = me.currentIteration;
389 if (iterCount < iter) {
390 me.startTime = new Date();
391 me.currentIteration = iterCount;
393 me.applyAnimator(me.target);
394 me.animations[me.keyframeStep].paused = false;
397 me.currentIteration = 0;
403 * Fire afteranimate event and end the animation. Usually called automatically when the
404 * animation reaches its final frame, but can also be called manually to pre-emptively
405 * stop and destroy the running animation.
409 me.fireEvent('afteranimate', me, me.startTime, new Date() - me.startTime);