]> git.proxmox.com Git - sencha-touch.git/blob - src/src/Anim.js
import Sencha Touch 2.4.2 source
[sencha-touch.git] / src / src / Anim.js
1 /**
2 * Ext.Anim is used to execute simple animations defined in {@link Ext.anims}. The {@link #run} method can take any of the
3 * properties defined below.
4 *
5 * Ext.Anim.run(this, 'fade', {
6 * out: false,
7 * autoClear: true
8 * });
9 *
10 * When using {@link Ext.Anim#run}, ensure you require {@link Ext.Anim} in your application. Either do this using {@link Ext#require}:
11 *
12 * Ext.requires('Ext.Anim');
13 *
14 * when using {@link Ext#setup}:
15 *
16 * Ext.setup({
17 * requires: ['Ext.Anim'],
18 * onReady: function() {
19 * //do something
20 * }
21 * });
22 *
23 * or when using {@link Ext#application}:
24 *
25 * Ext.application({
26 * requires: ['Ext.Anim'],
27 * launch: function() {
28 * //do something
29 * }
30 * });
31 *
32 * @singleton
33 */
34
35 Ext.define('Ext.Anim', {
36 isAnim: true,
37
38 /**
39 * @cfg {Boolean} disableAnimations
40 * `true` to disable animations.
41 */
42 disableAnimations: false,
43
44 defaultConfig: {
45 /**
46 * @cfg {Object} from
47 * An object of CSS values which the animation begins with. If you define a CSS property here, you must also
48 * define it in the {@link #to} config.
49 */
50 from: {},
51
52 /**
53 * @cfg {Object} to
54 * An object of CSS values which the animation ends with. If you define a CSS property here, you must also
55 * define it in the {@link #from} config.
56 */
57 to: {},
58
59 /**
60 * @cfg {Number} duration
61 * Time in milliseconds for the animation to last.
62 */
63 duration: 250,
64
65 /**
66 * @cfg {Number} delay Time to delay before starting the animation.
67 */
68 delay: 0,
69
70 /**
71 * @cfg {String} easing
72 * Valid values are 'ease', 'linear', ease-in', 'ease-out', 'ease-in-out', or a cubic-bezier curve as defined by CSS.
73 */
74 easing: 'ease-in-out',
75
76 /**
77 * @cfg {Boolean} autoClear
78 * `true` to remove all custom CSS defined in the {@link #to} config when the animation is over.
79 */
80 autoClear: true,
81
82 /**
83 * @cfg {Boolean} out
84 * `true` if you want the animation to slide out of the screen.
85 */
86 out: true,
87
88 /**
89 * @cfg {String} direction
90 * Valid values are: 'left', 'right', 'up', 'down', and `null`.
91 */
92 direction: null,
93
94 /**
95 * @cfg {Boolean} reverse
96 * `true` to reverse the animation direction. For example, if the animation direction was set to 'left', it would
97 * then use 'right'.
98 */
99 reverse: false
100 },
101
102 /**
103 * @cfg {Function} before
104 * Code to execute before starting the animation.
105 */
106
107 /**
108 * @cfg {Function} after
109 * Code to execute after the animation ends.
110 */
111
112 /**
113 * @cfg {Object} scope
114 * Scope to run the {@link #before} function in.
115 */
116
117 opposites: {
118 'left': 'right',
119 'right': 'left',
120 'up': 'down',
121 'down': 'up'
122 },
123
124 constructor: function(config) {
125 config = Ext.apply({}, config || {}, this.defaultConfig);
126 this.config = config;
127
128 this.callSuper([config]);
129
130 this.running = [];
131 },
132
133 initConfig: function(el, runConfig) {
134 var me = this,
135 config = Ext.apply({}, runConfig || {}, me.config);
136
137 config.el = el = Ext.get(el);
138
139 if (config.reverse && me.opposites[config.direction]) {
140 config.direction = me.opposites[config.direction];
141 }
142
143 if (me.config.before) {
144 me.config.before.call(config, el, config);
145 }
146
147 if (runConfig.before) {
148 runConfig.before.call(config.scope || config, el, config);
149 }
150
151 return config;
152 },
153
154 /**
155 * @ignore
156 */
157 run: function(el, config) {
158 el = Ext.get(el);
159 config = config || {};
160
161
162 var me = this,
163 style = el.dom.style,
164 property,
165 after = config.after;
166
167 if (me.running[el.id]) {
168 me.onTransitionEnd(null, el, {
169 config: config,
170 after: after
171 });
172 }
173
174 config = this.initConfig(el, config);
175
176 if (this.disableAnimations) {
177 for (property in config.to) {
178 if (!config.to.hasOwnProperty(property)) {
179 continue;
180 }
181 style[property] = config.to[property];
182 }
183 this.onTransitionEnd(null, el, {
184 config: config,
185 after: after
186 });
187 return me;
188 }
189
190 el.un('transitionend', me.onTransitionEnd, me);
191
192 style.webkitTransitionDuration = '0ms';
193 for (property in config.from) {
194 if (!config.from.hasOwnProperty(property)) {
195 continue;
196 }
197 style[property] = config.from[property];
198 }
199
200 setTimeout(function() {
201 // If this element has been destroyed since the timeout started, do nothing
202 if (!el.dom) {
203 return;
204 }
205
206 // If this is a 3d animation we have to set the perspective on the parent
207 if (config.is3d === true) {
208 el.parent().setStyle({
209 // See https://sencha.jira.com/browse/TOUCH-1498
210 '-webkit-perspective': '1200',
211 '-webkit-transform-style': 'preserve-3d'
212 });
213 }
214
215 style.webkitTransitionDuration = config.duration + 'ms';
216 style.webkitTransitionProperty = 'all';
217 style.webkitTransitionTimingFunction = config.easing;
218
219 // Bind our listener that fires after the animation ends
220 el.on('transitionend', me.onTransitionEnd, me, {
221 single: true,
222 config: config,
223 after: after
224 });
225
226 for (property in config.to) {
227 if (!config.to.hasOwnProperty(property)) {
228 continue;
229 }
230 style[property] = config.to[property];
231 }
232 }, config.delay || 5);
233
234 me.running[el.id] = config;
235 return me;
236 },
237
238 onTransitionEnd: function(ev, el, o) {
239 el = Ext.get(el);
240
241 if (this.running[el.id] === undefined) {
242 return;
243 }
244
245 var style = el.dom.style,
246 config = o.config,
247 me = this,
248 property;
249
250 if (config.autoClear) {
251 for (property in config.to) {
252 if (!config.to.hasOwnProperty(property) || config[property] === false) {
253 continue;
254 }
255 style[property] = '';
256 }
257 }
258
259 style.webkitTransitionDuration = null;
260 style.webkitTransitionProperty = null;
261 style.webkitTransitionTimingFunction = null;
262
263 if (config.is3d) {
264 el.parent().setStyle({
265 '-webkit-perspective': '',
266 '-webkit-transform-style': ''
267 });
268 }
269
270 if (me.config.after) {
271 me.config.after.call(config, el, config);
272 }
273
274 if (o.after) {
275 o.after.call(config.scope || me, el, config);
276 }
277
278 delete me.running[el.id];
279 }
280 }, function() {
281
282 Ext.Anim.seed = 1000;
283
284 /**
285 * Used to run an animation on a specific element. Use the config argument to customize the animation.
286 * @param {Ext.Element/HTMLElement} el The element to animate.
287 * @param {String} anim The animation type, defined in {@link Ext.anims}.
288 * @param {Object} config The config object for the animation.
289 * @method run
290 */
291 Ext.Anim.run = function(el, anim, config) {
292 if (el.isComponent) {
293 el = el.element;
294 } else {
295 el = Ext.get(el);
296 }
297
298 config = config || {};
299
300 if (anim.isAnim) {
301 anim.run(el, config);
302 }
303 else {
304 if (Ext.isObject(anim)) {
305 if (config.before && anim.before) {
306 config.before = Ext.createInterceptor(config.before, anim.before, anim.scope);
307 }
308 if (config.after && anim.after) {
309 config.after = Ext.createInterceptor(config.after, anim.after, anim.scope);
310 }
311 config = Ext.apply({}, config, anim);
312 anim = anim.type;
313 }
314
315 if (!Ext.anims[anim]) {
316 throw anim + ' is not a valid animation type.';
317 }
318 else {
319 // add el check to make sure dom exists.
320 if (el && el.dom) {
321 Ext.anims[anim].run(el, config);
322 }
323 }
324 }
325 };
326
327 /**
328 * @class Ext.anims
329 * Defines different types of animations.
330 *
331 * __Note:__ _flip_, _cube_, and _wipe_ animations do not work on Android.
332 *
333 * Please refer to {@link Ext.Anim} on how to use animations.
334 * @singleton
335 */
336 Ext.anims = {
337 /**
338 * Fade Animation
339 */
340 fade: new Ext.Anim({
341 type: 'fade',
342 before: function(el) {
343 var fromOpacity = 1,
344 toOpacity = 1,
345 curZ = el.getStyle('z-index') == 'auto' ? 0 : el.getStyle('z-index'),
346 zIndex = curZ;
347
348 if (this.out) {
349 toOpacity = 0;
350 } else {
351 zIndex = Math.abs(curZ) + 1;
352 fromOpacity = 0;
353 }
354
355 this.from = {
356 'opacity': fromOpacity,
357 'z-index': zIndex
358 };
359 this.to = {
360 'opacity': toOpacity,
361 'z-index': zIndex
362 };
363 }
364 }),
365
366 /**
367 * Slide Animation
368 */
369 slide: new Ext.Anim({
370 direction: 'left',
371 cover: false,
372 reveal: false,
373 opacity: false,
374 'z-index': false,
375
376 before: function(el) {
377 var currentZIndex = el.getStyle('z-index') == 'auto' ? 0 : el.getStyle('z-index'),
378 currentOpacity = el.getStyle('opacity'),
379 zIndex = currentZIndex + 1,
380 out = this.out,
381 direction = this.direction,
382 toX = 0,
383 toY = 0,
384 fromX = 0,
385 fromY = 0,
386 elH = el.getHeight(),
387 elW = el.getWidth();
388
389 if (direction == 'left' || direction == 'right') {
390 if (out) {
391 toX = -elW;
392 }
393 else {
394 fromX = elW;
395 }
396 }
397 else if (direction == 'up' || direction == 'down') {
398 if (out) {
399 toY = -elH;
400 }
401 else {
402 fromY = elH;
403 }
404 }
405
406 if (direction == 'right' || direction == 'down') {
407 toY *= -1;
408 toX *= -1;
409 fromY *= -1;
410 fromX *= -1;
411 }
412
413 if (this.cover && out) {
414 toX = 0;
415 toY = 0;
416 zIndex = currentZIndex;
417 }
418 else if (this.reveal && !out) {
419 fromX = 0;
420 fromY = 0;
421 zIndex = currentZIndex;
422 }
423
424 this.from = {
425 '-webkit-transform': 'translate3d(' + fromX + 'px, ' + fromY + 'px, 0)',
426 'z-index': zIndex,
427 'opacity': currentOpacity - 0.01
428 };
429 this.to = {
430 '-webkit-transform': 'translate3d(' + toX + 'px, ' + toY + 'px, 0)',
431 'z-index': zIndex,
432 'opacity': currentOpacity
433 };
434 }
435 }),
436
437 /**
438 * Pop Animation
439 */
440 pop: new Ext.Anim({
441 scaleOnExit: true,
442 before: function(el) {
443 var fromScale = 1,
444 toScale = 1,
445 fromOpacity = 1,
446 toOpacity = 1,
447 curZ = el.getStyle('z-index') == 'auto' ? 0 : el.getStyle('z-index'),
448 fromZ = curZ,
449 toZ = curZ;
450
451 if (!this.out) {
452 fromScale = 0.01;
453 fromZ = curZ + 1;
454 toZ = curZ + 1;
455 fromOpacity = 0;
456 }
457 else {
458 if (this.scaleOnExit) {
459 toScale = 0.01;
460 toOpacity = 0;
461 } else {
462 toOpacity = 0.8;
463 }
464 }
465
466 this.from = {
467 '-webkit-transform': 'scale(' + fromScale + ')',
468 '-webkit-transform-origin': '50% 50%',
469 'opacity': fromOpacity,
470 'z-index': fromZ
471 };
472
473 this.to = {
474 '-webkit-transform': 'scale(' + toScale + ')',
475 '-webkit-transform-origin': '50% 50%',
476 'opacity': toOpacity,
477 'z-index': toZ
478 };
479 }
480 }),
481
482 /**
483 * Flip Animation
484 */
485 flip: new Ext.Anim({
486 is3d: true,
487 direction: 'left',
488 before: function(el) {
489 var rotateProp = 'Y',
490 fromScale = 1,
491 toScale = 1,
492 fromRotate = 0,
493 toRotate = 0;
494
495 if (this.out) {
496 toRotate = -180;
497 toScale = 0.8;
498 }
499 else {
500 fromRotate = 180;
501 fromScale = 0.8;
502 }
503
504 if (this.direction == 'up' || this.direction == 'down') {
505 rotateProp = 'X';
506 }
507
508 if (this.direction == 'right' || this.direction == 'left') {
509 toRotate *= -1;
510 fromRotate *= -1;
511 }
512
513 this.from = {
514 '-webkit-transform': 'rotate' + rotateProp + '(' + fromRotate + 'deg) scale(' + fromScale + ')',
515 '-webkit-backface-visibility': 'hidden'
516 };
517 this.to = {
518 '-webkit-transform': 'rotate' + rotateProp + '(' + toRotate + 'deg) scale(' + toScale + ')',
519 '-webkit-backface-visibility': 'hidden'
520 };
521 }
522 }),
523
524 /**
525 * Cube Animation
526 */
527 cube: new Ext.Anim({
528 is3d: true,
529 direction: 'left',
530 style: 'outer',
531 before: function(el) {
532 var origin = '0% 0%',
533 fromRotate = 0,
534 toRotate = 0,
535 rotateProp = 'Y',
536 fromZ = 0,
537 toZ = 0,
538 elW = el.getWidth(),
539 elH = el.getHeight(),
540 showTranslateZ = true,
541 fromTranslate = ' translateX(0)',
542 toTranslate = '';
543
544 if (this.direction == 'left' || this.direction == 'right') {
545 if (this.out) {
546 origin = '100% 100%';
547 toZ = elW;
548 toRotate = -90;
549 } else {
550 origin = '0% 0%';
551 fromZ = elW;
552 fromRotate = 90;
553 }
554 } else if (this.direction == 'up' || this.direction == 'down') {
555 rotateProp = 'X';
556 if (this.out) {
557 origin = '100% 100%';
558 toZ = elH;
559 toRotate = 90;
560 } else {
561 origin = '0% 0%';
562 fromZ = elH;
563 fromRotate = -90;
564 }
565 }
566
567 if (this.direction == 'down' || this.direction == 'right') {
568 fromRotate *= -1;
569 toRotate *= -1;
570 origin = (origin == '0% 0%') ? '100% 100%': '0% 0%';
571 }
572
573 if (this.style == 'inner') {
574 fromZ *= -1;
575 toZ *= -1;
576 fromRotate *= -1;
577 toRotate *= -1;
578
579 if (!this.out) {
580 toTranslate = ' translateX(0px)';
581 origin = '0% 50%';
582 } else {
583 toTranslate = fromTranslate;
584 origin = '100% 50%';
585 }
586 }
587
588 this.from = {
589 '-webkit-transform': 'rotate' + rotateProp + '(' + fromRotate + 'deg)' + (showTranslateZ ? ' translateZ(' + fromZ + 'px)': '') + fromTranslate,
590 '-webkit-transform-origin': origin
591 };
592 this.to = {
593 '-webkit-transform': 'rotate' + rotateProp + '(' + toRotate + 'deg) translateZ(' + toZ + 'px)' + toTranslate,
594 '-webkit-transform-origin': origin
595 };
596 },
597 duration: 250
598 }),
599
600
601 /**
602 * Wipe Animation.
603 * Because of the amount of calculations involved, this animation is best used on small display
604 * changes or specifically for phone environments. Does not currently accept any parameters.
605 */
606 wipe: new Ext.Anim({
607 before: function(el) {
608 var curZ = el.getStyle('z-index'),
609 zIndex,
610 mask = '';
611
612 if (!this.out) {
613 zIndex = curZ + 1;
614 mask = '-webkit-gradient(linear, left bottom, right bottom, from(transparent), to(#000), color-stop(66%, #000), color-stop(33%, transparent))';
615
616 this.from = {
617 '-webkit-mask-image': mask,
618 '-webkit-mask-size': el.getWidth() * 3 + 'px ' + el.getHeight() + 'px',
619 'z-index': zIndex,
620 '-webkit-mask-position-x': 0
621 };
622 this.to = {
623 '-webkit-mask-image': mask,
624 '-webkit-mask-size': el.getWidth() * 3 + 'px ' + el.getHeight() + 'px',
625 'z-index': zIndex,
626 '-webkit-mask-position-x': -el.getWidth() * 2 + 'px'
627 };
628 }
629 },
630 duration: 500
631 })
632 };
633 });