2
Copyright (c) 2008, Yahoo! Inc. All rights reserved.
3
Code licensed under the BSD License:
4
http://developer.yahoo.net/yui/license.txt
7
YUI.add('anim-base', function(Y) {
10
* Y.Animation Utility.
15
* Handles animation _queueing and threading.
21
var RUNNING = 'running',
22
START_TIME = 'startTime',
23
ELAPSED_TIME = 'elapsedTime',
26
* @description fires when an animation begins.
27
* @param {Event} ev The start event.
34
* @description fires every frame of the animation.
35
* @param {Event} ev The tween event.
42
* @description fires after the animation completes.
43
* @param {Event} ev The end event.
49
REVERSE = 'reverse', // TODO: cleanup
50
ITERATION_COUNT = 'iterationCount',
58
var _setPrivate = function(anim, prop, val) {
59
if (typeof prop == 'string') {
60
anim._conf.add(prop, { value: val });
62
Y.each(prop, function(v, n) {
63
_setPrivate(anim, n, v);
68
Y.Anim.superclass.constructor.apply(this, arguments);
69
_instances[Y.stamp(this)] = this;
75
* Regex of properties that should use the default unit.
77
* @property RE_DEFAULT_UNIT
80
Y.Anim.RE_DEFAULT_UNIT = /^width|height|top|right|bottom|left|margin.*|padding.*|border.*$/i;
83
* The default unit to use with properties that pass the RE_DEFAULT_UNIT test.
85
* @property DEFAULT_UNIT
88
Y.Anim.DEFAULT_UNIT = 'px';
92
* Bucket for custom getters and setters
99
get: function(anim, attr) {
100
return anim._getOffset(attr);
105
Y.Anim.behaviors.top = Y.Anim.behaviors.left;
108
* The default setter to use when setting object properties.
110
* @property DEFAULT_SETTER
113
Y.Anim.DEFAULT_SETTER = function(anim, att, from, to, elapsed, duration, fn, unit) {
115
anim._node.setStyle(att, fn(elapsed, NUM(from), NUM(to) - NUM(from), duration) + unit);
119
* The default getter to use when getting object properties.
121
* @property DEFAULT_GETTER
124
Y.Anim.DEFAULT_GETTER = function(anim, prop) {
125
return anim._node.getComputedStyle(prop);
130
* The object to be animated.
135
set: function(node) {
136
node = Y.Node.get(node);
139
Y.fail('Y.Anim: invalid node: ' + node);
146
* The length of the animation. Defaults to "1" (second).
147
* @attribute duration
155
* The method that will provide values to the attribute(s) during the animation.
156
* Defaults to "Easing.easeNone".
161
value: function (t, b, c, d) {
162
return c * t / d + b; // linear easing
167
* The starting values for the animated properties.
168
* Fields may be strings, numbers, or functions.
169
* If a function is used, the return value becomes the from value.
170
* If no from value is specified, the DEFAULT_GETTER will be used.
177
* The ending values for the animated properties.
178
* Fields may be strings, numbers, or functions.
185
* Date stamp for the first frame of the animation.
186
* @attribute startTime
197
* Current time the animation has been running.
198
* @attribute elapsedTime
209
* Whether or not the animation is currently running.
217
return !!_running[Y.stamp(this)];
224
* The number of times the animation should run
225
* @attribute iterations
234
* The number of iterations that have occurred.
235
* Resets when an animation ends (reaches iteration count or stop() called).
236
* @attribute iterationCount
247
* How iterations of the animation should behave.
248
* Possible values are "normal" and "alternate".
249
* Normal will repeat the animation, alternate will reverse on every other pass.
251
* @attribute direction
256
value: 'normal' // | alternate (fwd on odd, rev on even per spec)
260
* Whether or not the animation is currently paused.
272
* If true, animation begins from last frame
285
* Runs all animation instances.
289
Y.Anim.run = function() {
290
for (var i in _instances) {
291
if (_instances[i].run) {
298
* Pauses all animation instances.
302
Y.Anim.pause = function() {
303
for (var i in _running) { // stop timer if nothing running
304
if (_running[i].pause) {
312
* Stops all animation instances.
316
Y.Anim.stop = function() {
317
for (var i in _running) { // stop timer if nothing running
318
if (_running[i].stop) {
325
Y.Anim._startTimer = function() {
327
_timer = setInterval(Y.Anim._runFrame, 1);
331
Y.Anim._stopTimer = function() {
332
clearInterval(_timer);
337
* Called per Interval to handle each animation frame.
342
Y.Anim._runFrame = function() {
344
for (var anim in _running) {
345
if (_running[anim]._runFrame) {
347
_running[anim]._runFrame();
356
Y.Anim.RE_UNITS = /^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/;
360
* Starts or resumes an animation.
361
* percent start time marker.
366
if (!this.get(RUNNING)) {
368
} else if (this.get(PAUSED)) {
375
* Pauses the animation and
376
* freezes it in its current state and time.
377
* Calling run() will continue where it left off.
382
if (this.get(RUNNING)) {
389
* Stops the animation and resets its time.
393
stop: function(finish) {
394
if (this.get(RUNNING) || this.get(PAUSED)) {
403
_setPrivate(this, START_TIME, new Date() - this.get(ELAPSED_TIME));
404
this._actualFrames = 0;
405
if (!this.get(PAUSED)) {
408
_running[Y.stamp(this)] = this;
409
Y.Anim._startTimer();
415
_setPrivate(this, START_TIME, null);
416
_setPrivate(this, PAUSED, true);
417
delete _running[Y.stamp(this)];
421
* @description fires when an animation is paused.
422
* @param {Event} ev The pause event.
428
_resume: function() {
429
_setPrivate(this, PAUSED, false);
430
_running[Y.stamp(this)] = this;
434
* @description fires when an animation is resumed (run from pause).
435
* @param {Event} ev The pause event.
441
_end: function(finish) {
442
_setPrivate(this, START_TIME, null);
443
_setPrivate(this, ELAPSED_TIME, 0);
444
_setPrivate(this, PAUSED, false);
445
//_setPrivate(this, REVERSE, false);
447
delete _running[Y.stamp(this)];
448
this.fire(END, {elapsed: this.get(ELAPSED_TIME)});
451
_runFrame: function() {
452
var attr = this._runtimeAttr,
453
customAttr = Y.Anim.behaviors,
454
easing = attr.easing,
456
t = new Date() - this.get(START_TIME),
457
reversed = this.get(REVERSE),
469
for (var i in attr) {
472
setter = (i in customAttr && 'set' in customAttr[i]) ?
473
customAttr[i].set : Y.Anim.DEFAULT_SETTER;
476
setter(this, i, attribute.from, attribute.to, t, d, easing, attribute.unit);
477
} else { // ensure final frame value is set
478
// TODO: handle keyframes
479
setter(this, i, attribute.from, attribute.to, lastFrame, d, easing, attribute.unit);
484
this._actualFrames += 1;
485
_setPrivate(this, ELAPSED_TIME, t);
493
_lastFrame: function() {
494
var iter = this.get('iterations'),
495
iterCount = this.get(ITERATION_COUNT);
498
if (iter === 'infinite' || iterCount < iter) {
499
if (this.get('direction') === 'alternate') {
500
this.set(REVERSE, !this.get(REVERSE)); // flip it
504
* @description fires when an animation begins an iteration.
505
* @param {Event} ev The iteration event.
508
this.fire('iteration');
514
_setPrivate(this, START_TIME, new Date());
515
_setPrivate(this, ITERATION_COUNT, iterCount);
518
_initAttr: function() {
519
var from = this.get('from') || {},
520
to = this.get('to') || {},
521
dur = this.get('duration') * 1000,
522
node = this.get(NODE),
523
easing = this.get('easing') || {},
525
customAttr = Y.Anim.behaviors,
528
Y.each(to, function(val, name) {
529
if (typeof val === 'function') {
530
val = val.call(this, node);
534
if (begin === undefined) {
535
begin = (name in customAttr && 'get' in customAttr[name]) ?
536
customAttr[name].get(this, name) : Y.Anim.DEFAULT_GETTER(this, name);
537
} else if (typeof begin === 'function') {
538
begin = begin.call(this, node);
541
var mFrom = Y.Anim.RE_UNITS.exec(begin);
542
var mTo = Y.Anim.RE_UNITS.exec(val);
544
begin = mFrom ? mFrom[1] : begin;
545
var end = mTo ? mTo[1] : val,
546
unit = mTo ? mTo[2] : mFrom ? mFrom[2] : ''; // one might be zero TODO: mixed units
548
if (!unit && Y.Anim.RE_DEFAULT_UNIT.test(name)) {
549
unit = Y.Anim.DEFAULT_UNIT;
552
if (!begin || !end) {
553
Y.fail('invalid "from" or "to" for "' + name + '"', 'Anim');
564
attr.easing = easing;
568
this._runtimeAttr = attr;
572
// TODO: move to computedStyle? (browsers dont agree on default computed offsets)
573
_getOffset: function(attr) {
574
var node = this._node,
575
val = node.getComputedStyle(attr),
576
get = (attr === 'left') ? 'getX': 'getY',
577
set = (attr === 'left') ? 'setX': 'setY';
579
if (val === 'auto') {
580
var position = node.getStyle('position');
581
if (position === 'absolute' || position === 'fixed') {
593
Y.extend(Y.Anim, Y.Base, proto);
596
}, '3.0.0pr1' ,{requires:['base', 'node']});