Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('anim-base', function (Y, NAME) {
2
 
3
/**
4
* The Animation Utility provides an API for creating advanced transitions.
5
* @module anim
6
*/
7
 
8
/**
9
* Provides the base Anim class, for animating numeric properties.
10
*
11
* @module anim
12
* @submodule anim-base
13
*/
14
 
15
    /**
16
     * A class for constructing animation instances.
17
     * @class Anim
18
     * @for Anim
19
     * @constructor
20
     * @extends Base
21
     */
22
 
23
    var RUNNING = 'running',
24
        START_TIME = 'startTime',
25
        ELAPSED_TIME = 'elapsedTime',
26
        /**
27
        * @for Anim
28
        * @event start
29
        * @description fires when an animation begins.
30
        * @param {Event} ev The start event.
31
        * @type Event.Custom
32
        */
33
        START = 'start',
34
 
35
        /**
36
        * @event tween
37
        * @description fires every frame of the animation.
38
        * @param {Event} ev The tween event.
39
        * @type Event.Custom
40
        */
41
        TWEEN = 'tween',
42
 
43
        /**
44
        * @event end
45
        * @description fires after the animation completes.
46
        * @param {Event} ev The end event.
47
        * @type Event.Custom
48
        */
49
        END = 'end',
50
        NODE = 'node',
51
        PAUSED = 'paused',
52
        REVERSE = 'reverse', // TODO: cleanup
53
        ITERATION_COUNT = 'iterationCount',
54
 
55
        NUM = Number;
56
 
57
    var _running = {},
58
        _timer;
59
 
60
    Y.Anim = function() {
61
        Y.Anim.superclass.constructor.apply(this, arguments);
62
        Y.Anim._instances[Y.stamp(this)] = this;
63
    };
64
 
65
    Y.Anim.NAME = 'anim';
66
 
67
    Y.Anim._instances = {};
68
 
69
    /**
70
     * Regex of properties that should use the default unit.
71
     *
72
     * @property RE_DEFAULT_UNIT
73
     * @static
74
     */
75
    Y.Anim.RE_DEFAULT_UNIT = /^width|height|top|right|bottom|left|margin.*|padding.*|border.*$/i;
76
 
77
    /**
78
     * The default unit to use with properties that pass the RE_DEFAULT_UNIT test.
79
     *
80
     * @property DEFAULT_UNIT
81
     * @static
82
     */
83
    Y.Anim.DEFAULT_UNIT = 'px';
84
 
85
    Y.Anim.DEFAULT_EASING = function (t, b, c, d) {
86
        return c * t / d + b; // linear easing
87
    };
88
 
89
    /**
90
     * Time in milliseconds passed to setInterval for frame processing
91
     *
92
     * @property intervalTime
93
     * @default 20
94
     * @static
95
     */
96
    Y.Anim._intervalTime = 20;
97
 
98
    /**
99
     * Bucket for custom getters and setters
100
     *
101
     * @property behaviors
102
     * @static
103
     */
104
    Y.Anim.behaviors = {
105
        left: {
106
            get: function(anim, attr) {
107
                return anim._getOffset(attr);
108
            }
109
        }
110
    };
111
 
112
    Y.Anim.behaviors.top = Y.Anim.behaviors.left;
113
 
114
    /**
115
     * The default setter to use when setting object properties.
116
     *
117
     * @property DEFAULT_SETTER
118
     * @static
119
     */
120
    Y.Anim.DEFAULT_SETTER = function(anim, att, from, to, elapsed, duration, fn, unit) {
121
        var node = anim._node,
122
            domNode = node._node,
123
            val = fn(elapsed, NUM(from), NUM(to) - NUM(from), duration);
124
 
125
        if (domNode) {
126
            if ('style' in domNode && (att in domNode.style || att in Y.DOM.CUSTOM_STYLES)) {
127
                unit = unit || '';
128
                node.setStyle(att, val + unit);
129
            } else if ('attributes' in domNode && att in domNode.attributes) {
130
                node.setAttribute(att, val);
131
            } else if (att in domNode) {
132
                domNode[att] = val;
133
            }
134
        } else if (node.set) {
135
            node.set(att, val);
136
        } else if (att in node) {
137
            node[att] = val;
138
        }
139
    };
140
 
141
    /**
142
     * The default getter to use when getting object properties.
143
     *
144
     * @property DEFAULT_GETTER
145
     * @static
146
     */
147
    Y.Anim.DEFAULT_GETTER = function(anim, att) {
148
        var node = anim._node,
149
            domNode = node._node,
150
            val = '';
151
 
152
        if (domNode) {
153
            if ('style' in domNode && (att in domNode.style || att in Y.DOM.CUSTOM_STYLES)) {
154
                val = node.getComputedStyle(att);
155
            } else if ('attributes' in domNode && att in domNode.attributes) {
156
                val = node.getAttribute(att);
157
            } else if (att in domNode) {
158
                val = domNode[att];
159
            }
160
        } else if (node.get) {
161
            val = node.get(att);
162
        } else if (att in node) {
163
            val = node[att];
164
        }
165
 
166
        return val;
167
    };
168
 
169
    Y.Anim.ATTRS = {
170
        /**
171
         * The object to be animated.
172
         * @attribute node
173
         * @type Node
174
         */
175
        node: {
176
            setter: function(node) {
177
                if (node) {
178
                    if (typeof node === 'string' || node.nodeType) {
179
                        node = Y.one(node);
180
                    }
181
                }
182
 
183
                this._node = node;
184
                if (!node) {
185
                    Y.log(node + ' is not a valid node', 'warn', 'Anim');
186
                }
187
                return node;
188
            }
189
        },
190
 
191
        /**
192
         * The length of the animation.  Defaults to "1" (second).
193
         * @attribute duration
194
         * @type NUM
195
         */
196
        duration: {
197
            value: 1
198
        },
199
 
200
        /**
201
         * The method that will provide values to the attribute(s) during the animation.
202
         * Defaults to "Easing.easeNone".
203
         * @attribute easing
204
         * @type Function
205
         */
206
        easing: {
207
            value: Y.Anim.DEFAULT_EASING,
208
 
209
            setter: function(val) {
210
                if (typeof val === 'string' && Y.Easing) {
211
                    return Y.Easing[val];
212
                }
213
            }
214
        },
215
 
216
        /**
217
         * The starting values for the animated properties.
218
         *
219
         * Fields may be strings, numbers, or functions.
220
         * If a function is used, the return value becomes the from value.
221
         * If no from value is specified, the DEFAULT_GETTER will be used.
222
         * Supports any unit, provided it matches the "to" (or default)
223
         * unit (e.g. `{width: '10em', color: 'rgb(0, 0, 0)', borderColor: '#ccc'}`).
224
         *
225
         * If using the default ('px' for length-based units), the unit may be omitted
226
         * (e.g. `{width: 100}, borderColor: 'ccc'}`, which defaults to pixels
227
         * and hex, respectively).
228
         *
229
         * @attribute from
230
         * @type Object
231
         */
232
        from: {},
233
 
234
        /**
235
         * The ending values for the animated properties.
236
         *
237
         * Fields may be strings, numbers, or functions.
238
         * Supports any unit, provided it matches the "from" (or default)
239
         * unit (e.g. `{width: '50%', color: 'red', borderColor: '#ccc'}`).
240
         *
241
         * If using the default ('px' for length-based units), the unit may be omitted
242
         * (e.g. `{width: 100, borderColor: 'ccc'}`, which defaults to pixels
243
         * and hex, respectively).
244
         *
245
         * @attribute to
246
         * @type Object
247
         */
248
        to: {},
249
 
250
        /**
251
         * Date stamp for the first frame of the animation.
252
         * @attribute startTime
253
         * @type Int
254
         * @default 0
255
         * @readOnly
256
         */
257
        startTime: {
258
            value: 0,
259
            readOnly: true
260
        },
261
 
262
        /**
263
         * Current time the animation has been running.
264
         * @attribute elapsedTime
265
         * @type Int
266
         * @default 0
267
         * @readOnly
268
         */
269
        elapsedTime: {
270
            value: 0,
271
            readOnly: true
272
        },
273
 
274
        /**
275
         * Whether or not the animation is currently running.
276
         * @attribute running
277
         * @type Boolean
278
         * @default false
279
         * @readOnly
280
         */
281
        running: {
282
            getter: function() {
283
                return !!_running[Y.stamp(this)];
284
            },
285
            value: false,
286
            readOnly: true
287
        },
288
 
289
        /**
290
         * The number of times the animation should run
291
         * @attribute iterations
292
         * @type Int
293
         * @default 1
294
         */
295
        iterations: {
296
            value: 1
297
        },
298
 
299
        /**
300
         * The number of iterations that have occurred.
301
         * Resets when an animation ends (reaches iteration count or stop() called).
302
         * @attribute iterationCount
303
         * @type Int
304
         * @default 0
305
         * @readOnly
306
         */
307
        iterationCount: {
308
            value: 0,
309
            readOnly: true
310
        },
311
 
312
        /**
313
         * How iterations of the animation should behave.
314
         * Possible values are "normal" and "alternate".
315
         * Normal will repeat the animation, alternate will reverse on every other pass.
316
         *
317
         * @attribute direction
318
         * @type String
319
         * @default "normal"
320
         */
321
        direction: {
322
            value: 'normal' // | alternate (fwd on odd, rev on even per spec)
323
        },
324
 
325
        /**
326
         * Whether or not the animation is currently paused.
327
         * @attribute paused
328
         * @type Boolean
329
         * @default false
330
         * @readOnly
331
         */
332
        paused: {
333
            readOnly: true,
334
            value: false
335
        },
336
 
337
        /**
338
         * If true, the `from` and `to` attributes are swapped,
339
         * and the animation is then run starting from `from`.
340
         * @attribute reverse
341
         * @type Boolean
342
         * @default false
343
         */
344
        reverse: {
345
            value: false
346
        }
347
 
348
 
349
    };
350
 
351
    /**
352
     * Runs all animation instances.
353
     * @method run
354
     * @static
355
     */
356
    Y.Anim.run = function() {
357
        var instances = Y.Anim._instances,
358
            i;
359
        for (i in instances) {
360
            if (instances[i].run) {
361
                instances[i].run();
362
            }
363
        }
364
    };
365
 
366
    /**
367
     * Pauses all animation instances.
368
     * @method pause
369
     * @static
370
     */
371
    Y.Anim.pause = function() {
372
        for (var i in _running) { // stop timer if nothing running
373
            if (_running[i].pause) {
374
                _running[i].pause();
375
            }
376
        }
377
 
378
        Y.Anim._stopTimer();
379
    };
380
 
381
    /**
382
     * Stops all animation instances.
383
     * @method stop
384
     * @static
385
     */
386
    Y.Anim.stop = function() {
387
        for (var i in _running) { // stop timer if nothing running
388
            if (_running[i].stop) {
389
                _running[i].stop();
390
            }
391
        }
392
        Y.Anim._stopTimer();
393
    };
394
 
395
    Y.Anim._startTimer = function() {
396
        if (!_timer) {
397
            _timer = setInterval(Y.Anim._runFrame, Y.Anim._intervalTime);
398
        }
399
    };
400
 
401
    Y.Anim._stopTimer = function() {
402
        clearInterval(_timer);
403
        _timer = 0;
404
    };
405
 
406
    /**
407
     * Called per Interval to handle each animation frame.
408
     * @method _runFrame
409
     * @private
410
     * @static
411
     */
412
    Y.Anim._runFrame = function() {
413
        var done = true,
414
            anim;
415
        for (anim in _running) {
416
            if (_running[anim]._runFrame) {
417
                done = false;
418
                _running[anim]._runFrame();
419
            }
420
        }
421
 
422
        if (done) {
423
            Y.Anim._stopTimer();
424
        }
425
    };
426
 
427
    Y.Anim.RE_UNITS = /^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/;
428
 
429
    var proto = {
430
        /**
431
         * Starts or resumes an animation.
432
         * @method run
433
         * @chainable
434
         */
435
        run: function() {
436
            if (this.get(PAUSED)) {
437
                this._resume();
438
            } else if (!this.get(RUNNING)) {
439
                this._start();
440
            }
441
            return this;
442
        },
443
 
444
        /**
445
         * Pauses the animation and
446
         * freezes it in its current state and time.
447
         * Calling run() will continue where it left off.
448
         * @method pause
449
         * @chainable
450
         */
451
        pause: function() {
452
            if (this.get(RUNNING)) {
453
                this._pause();
454
            }
455
            return this;
456
        },
457
 
458
        /**
459
         * Stops the animation and resets its time.
460
         * @method stop
461
         * @param {Boolean} finish If true, the animation will move to the last frame
462
         * @chainable
463
         */
464
        stop: function(finish) {
465
            if (this.get(RUNNING) || this.get(PAUSED)) {
466
                this._end(finish);
467
            }
468
            return this;
469
        },
470
 
471
        _added: false,
472
 
473
        _start: function() {
474
            this._set(START_TIME, new Date() - this.get(ELAPSED_TIME));
475
            this._actualFrames = 0;
476
            if (!this.get(PAUSED)) {
477
                this._initAnimAttr();
478
            }
479
            _running[Y.stamp(this)] = this;
480
            Y.Anim._startTimer();
481
 
482
            this.fire(START);
483
        },
484
 
485
        _pause: function() {
486
            this._set(START_TIME, null);
487
            this._set(PAUSED, true);
488
            delete _running[Y.stamp(this)];
489
 
490
            /**
491
            * @event pause
492
            * @description fires when an animation is paused.
493
            * @param {Event} ev The pause event.
494
            * @type Event.Custom
495
            */
496
            this.fire('pause');
497
        },
498
 
499
        _resume: function() {
500
            this._set(PAUSED, false);
501
            _running[Y.stamp(this)] = this;
502
            this._set(START_TIME, new Date() - this.get(ELAPSED_TIME));
503
            Y.Anim._startTimer();
504
 
505
            /**
506
            * @event resume
507
            * @description fires when an animation is resumed (run from pause).
508
            * @param {Event} ev The pause event.
509
            * @type Event.Custom
510
            */
511
            this.fire('resume');
512
        },
513
 
514
        _end: function(finish) {
515
            var duration = this.get('duration') * 1000;
516
            if (finish) { // jump to last frame
517
                this._runAttrs(duration, duration, this.get(REVERSE));
518
            }
519
 
520
            this._set(START_TIME, null);
521
            this._set(ELAPSED_TIME, 0);
522
            this._set(PAUSED, false);
523
 
524
            delete _running[Y.stamp(this)];
525
            this.fire(END, {elapsed: this.get(ELAPSED_TIME)});
526
        },
527
 
528
        _runFrame: function() {
529
            var d = this._runtimeAttr.duration,
530
                t = new Date() - this.get(START_TIME),
531
                reverse = this.get(REVERSE),
532
                done = (t >= d);
533
 
534
            this._runAttrs(t, d, reverse);
535
            this._actualFrames += 1;
536
            this._set(ELAPSED_TIME, t);
537
 
538
            this.fire(TWEEN);
539
            if (done) {
540
                this._lastFrame();
541
            }
542
        },
543
 
544
        _runAttrs: function(t, d, reverse) {
545
            var attr = this._runtimeAttr,
546
                customAttr = Y.Anim.behaviors,
547
                easing = attr.easing,
548
                lastFrame = d,
549
                done = false,
550
                attribute,
551
                setter,
552
                i;
553
 
554
            if (t >= d) {
555
                done = true;
556
            }
557
 
558
            if (reverse) {
559
                t = d - t;
560
                lastFrame = 0;
561
            }
562
 
563
            for (i in attr) {
564
                if (attr[i].to) {
565
                    attribute = attr[i];
566
                    setter = (i in customAttr && 'set' in customAttr[i]) ?
567
                            customAttr[i].set : Y.Anim.DEFAULT_SETTER;
568
 
569
                    if (!done) {
570
                        setter(this, i, attribute.from, attribute.to, t, d, easing, attribute.unit);
571
                    } else {
572
                        setter(this, i, attribute.from, attribute.to, lastFrame, d, easing, attribute.unit);
573
                    }
574
                }
575
            }
576
 
577
 
578
        },
579
 
580
        _lastFrame: function() {
581
            var iter = this.get('iterations'),
582
                iterCount = this.get(ITERATION_COUNT);
583
 
584
            iterCount += 1;
585
            if (iter === 'infinite' || iterCount < iter) {
586
                if (this.get('direction') === 'alternate') {
587
                    this.set(REVERSE, !this.get(REVERSE)); // flip it
588
                }
589
                /**
590
                * @event iteration
591
                * @description fires when an animation begins an iteration.
592
                * @param {Event} ev The iteration event.
593
                * @type Event.Custom
594
                */
595
                this.fire('iteration');
596
            } else {
597
                iterCount = 0;
598
                this._end();
599
            }
600
 
601
            this._set(START_TIME, new Date());
602
            this._set(ITERATION_COUNT, iterCount);
603
        },
604
 
605
        _initAnimAttr: function() {
606
            var from = this.get('from') || {},
607
                to = this.get('to') || {},
608
                attr = {
609
                    duration: this.get('duration') * 1000,
610
                    easing: this.get('easing')
611
                },
612
                customAttr = Y.Anim.behaviors,
613
                node = this.get(NODE), // implicit attr init
614
                unit, begin, end;
615
 
616
            Y.each(to, function(val, name) {
617
                if (typeof val === 'function') {
618
                    val = val.call(this, node);
619
                }
620
 
621
                begin = from[name];
622
                if (begin === undefined) {
623
                    begin = (name in customAttr && 'get' in customAttr[name])  ?
624
                            customAttr[name].get(this, name) : Y.Anim.DEFAULT_GETTER(this, name);
625
                } else if (typeof begin === 'function') {
626
                    begin = begin.call(this, node);
627
                }
628
 
629
                var mFrom = Y.Anim.RE_UNITS.exec(begin),
630
                    mTo = Y.Anim.RE_UNITS.exec(val);
631
 
632
                begin = mFrom ? mFrom[1] : begin;
633
                end = mTo ? mTo[1] : val;
634
                unit = mTo ? mTo[2] : mFrom ?  mFrom[2] : ''; // one might be zero TODO: mixed units
635
 
636
                if (!unit && Y.Anim.RE_DEFAULT_UNIT.test(name)) {
637
                    unit = Y.Anim.DEFAULT_UNIT;
638
                }
639
 
640
                if (!begin || !end) {
641
                    Y.error('invalid "from" or "to" for "' + name + '"', 'Anim');
642
                    return;
643
                }
644
 
645
                attr[name] = {
646
                    from: Y.Lang.isObject(begin) ? Y.clone(begin) : begin,
647
                    to: end,
648
                    unit: unit
649
                };
650
 
651
            }, this);
652
 
653
            this._runtimeAttr = attr;
654
        },
655
 
656
 
657
        // TODO: move to computedStyle? (browsers dont agree on default computed offsets)
658
        _getOffset: function(attr) {
659
            var node = this._node,
660
                val = node.getComputedStyle(attr),
661
                get = (attr === 'left') ? 'getX': 'getY',
662
                set = (attr === 'left') ? 'setX': 'setY',
663
                position;
664
 
665
            if (val === 'auto') {
666
                position = node.getStyle('position');
667
                if (position === 'absolute' || position === 'fixed') {
668
                    val = node[get]();
669
                    node[set](val);
670
                } else {
671
                    val = 0;
672
                }
673
            }
674
 
675
            return val;
676
        },
677
 
678
        destructor: function() {
679
            delete Y.Anim._instances[Y.stamp(this)];
680
        }
681
    };
682
 
683
    Y.extend(Y.Anim, Y.Base, proto);
684
 
685
 
686
}, '3.18.1', {"requires": ["base-base", "node-style", "color-base"]});