/**
* @license
* Copyright The Closure Library Authors.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview A class which automatically plays through a queue of
* animations. AnimationParallelQueue and AnimationSerialQueue provide
* specific implementations of the abstract class AnimationQueue.
*
* @see ../demos/animationqueue.html
*/
goog.provide('goog.fx.AnimationParallelQueue');
goog.provide('goog.fx.AnimationQueue');
goog.provide('goog.fx.AnimationSerialQueue');
goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.events');
goog.require('goog.fx.Animation');
goog.require('goog.fx.Transition');
goog.require('goog.fx.TransitionBase');
goog.requireType('goog.events.Event');
/**
* Constructor for AnimationQueue object.
*
* @constructor
* @struct
* @extends {goog.fx.TransitionBase}
*/
goog.fx.AnimationQueue = function() {
'use strict';
goog.fx.AnimationQueue.base(this, 'constructor');
/**
* An array holding all animations in the queue.
* @type {Array<goog.fx.TransitionBase>}
* @protected
*/
this.queue = [];
};
goog.inherits(goog.fx.AnimationQueue, goog.fx.TransitionBase);
/**
* Pushes an Animation to the end of the queue.
* @param {goog.fx.TransitionBase} animation The animation to add to the queue.
*/
goog.fx.AnimationQueue.prototype.add = function(animation) {
'use strict';
goog.asserts.assert(
this.isStopped(),
'Not allowed to add animations to a running animation queue.');
if (goog.array.contains(this.queue, animation)) {
return;
}
this.queue.push(animation);
goog.events.listen(
animation, goog.fx.Transition.EventType.FINISH, this.onAnimationFinish,
false, this);
};
/**
* Removes an Animation from the queue.
* @param {goog.fx.Animation} animation The animation to remove.
*/
goog.fx.AnimationQueue.prototype.remove = function(animation) {
'use strict';
goog.asserts.assert(
this.isStopped(),
'Not allowed to remove animations from a running animation queue.');
if (goog.array.remove(this.queue, animation)) {
goog.events.unlisten(
animation, goog.fx.Transition.EventType.FINISH, this.onAnimationFinish,
false, this);
}
};
/**
* Handles the event that an animation has finished.
* @param {goog.events.Event} e The finishing event.
* @protected
*/
goog.fx.AnimationQueue.prototype.onAnimationFinish = goog.abstractMethod;
/**
* Disposes of the animations.
* @override
*/
goog.fx.AnimationQueue.prototype.disposeInternal = function() {
'use strict';
this.queue.forEach(function(animation) {
'use strict';
animation.dispose();
});
this.queue.length = 0;
goog.fx.AnimationQueue.base(this, 'disposeInternal');
};
/**
* Constructor for AnimationParallelQueue object.
* @constructor
* @struct
* @extends {goog.fx.AnimationQueue}
*/
goog.fx.AnimationParallelQueue = function() {
'use strict';
goog.fx.AnimationParallelQueue.base(this, 'constructor');
/**
* Number of finished animations.
* @type {number}
* @private
*/
this.finishedCounter_ = 0;
};
goog.inherits(goog.fx.AnimationParallelQueue, goog.fx.AnimationQueue);
/** @override */
goog.fx.AnimationParallelQueue.prototype.play = function(opt_restart) {
'use strict';
if (this.queue.length == 0) {
return false;
}
if (opt_restart || this.isStopped()) {
this.finishedCounter_ = 0;
this.onBegin();
} else if (this.isPlaying()) {
return false;
}
this.onPlay();
if (this.isPaused()) {
this.onResume();
}
var resuming = this.isPaused() && !opt_restart;
this.startTime = goog.now();
this.endTime = null;
this.setStatePlaying();
this.queue.forEach(function(anim) {
'use strict';
if (!resuming || anim.isPaused()) {
anim.play(opt_restart);
}
});
return true;
};
/** @override */
goog.fx.AnimationParallelQueue.prototype.pause = function() {
'use strict';
if (this.isPlaying()) {
this.queue.forEach(function(anim) {
'use strict';
if (anim.isPlaying()) {
anim.pause();
}
});
this.setStatePaused();
this.onPause();
}
};
/** @override */
goog.fx.AnimationParallelQueue.prototype.stop = function(opt_gotoEnd) {
'use strict';
this.queue.forEach(function(anim) {
'use strict';
if (!anim.isStopped()) {
anim.stop(opt_gotoEnd);
}
});
this.setStateStopped();
this.endTime = goog.now();
this.onStop();
this.onEnd();
};
/** @override */
goog.fx.AnimationParallelQueue.prototype.onAnimationFinish = function(e) {
'use strict';
this.finishedCounter_++;
if (this.finishedCounter_ == this.queue.length) {
this.endTime = goog.now();
this.setStateStopped();
this.onFinish();
this.onEnd();
}
};
/**
* Constructor for AnimationSerialQueue object.
* @constructor
* @struct
* @extends {goog.fx.AnimationQueue}
*/
goog.fx.AnimationSerialQueue = function() {
'use strict';
goog.fx.AnimationSerialQueue.base(this, 'constructor');
/**
* Current animation in queue currently active.
* @type {number}
* @private
*/
this.current_ = 0;
};
goog.inherits(goog.fx.AnimationSerialQueue, goog.fx.AnimationQueue);
/** @override */
goog.fx.AnimationSerialQueue.prototype.play = function(opt_restart) {
'use strict';
if (this.queue.length == 0) {
return false;
}
if (opt_restart || this.isStopped()) {
if (this.current_ < this.queue.length &&
!this.queue[this.current_].isStopped()) {
this.queue[this.current_].stop(false);
}
this.current_ = 0;
this.onBegin();
} else if (this.isPlaying()) {
return false;
}
this.onPlay();
if (this.isPaused()) {
this.onResume();
}
this.startTime = goog.now();
this.endTime = null;
this.setStatePlaying();
this.queue[this.current_].play(opt_restart);
return true;
};
/** @override */
goog.fx.AnimationSerialQueue.prototype.pause = function() {
'use strict';
if (this.isPlaying()) {
this.queue[this.current_].pause();
this.setStatePaused();
this.onPause();
}
};
/** @override */
goog.fx.AnimationSerialQueue.prototype.stop = function(opt_gotoEnd) {
'use strict';
this.setStateStopped();
this.endTime = goog.now();
if (opt_gotoEnd) {
for (var i = this.current_; i < this.queue.length; ++i) {
var anim = this.queue[i];
// If the animation is stopped, start it to initiate rendering. This
// might be needed to make the next line work.
if (anim.isStopped()) anim.play();
// If the animation is not done, stop it and go to the end state of the
// animation.
if (!anim.isStopped()) anim.stop(true);
}
} else if (this.current_ < this.queue.length) {
this.queue[this.current_].stop(false);
}
this.onStop();
this.onEnd();
};
/** @override */
goog.fx.AnimationSerialQueue.prototype.onAnimationFinish = function(e) {
'use strict';
if (this.isPlaying()) {
this.current_++;
if (this.current_ < this.queue.length) {
this.queue[this.current_].play();
} else {
this.endTime = goog.now();
this.setStateStopped();
this.onFinish();
this.onEnd();
}
}
};