Path: blob/trunk/third_party/closure/goog/fx/anim/anim.js
4143 views
/**1* @license2* Copyright The Closure Library Authors.3* SPDX-License-Identifier: Apache-2.04*/56/**7* @fileoverview Basic animation controls.8*/9goog.provide('goog.fx.anim');10goog.provide('goog.fx.anim.Animated');1112goog.require('goog.async.AnimationDelay');13goog.require('goog.async.Delay');14goog.require('goog.dispose');15goog.require('goog.object');16171819/**20* An interface for programatically animated objects. I.e. rendered in21* javascript frame by frame.22*23* @interface24*/25goog.fx.anim.Animated = function() {};262728/**29* Function called when a frame is requested for the animation.30*31* @param {number} now Current time in milliseconds.32*/33goog.fx.anim.Animated.prototype.onAnimationFrame;343536/**37* Default wait timeout for animations (in milliseconds). Only used for timed38* animation, which uses a timer (setTimeout) to schedule animation.39*40* @type {number}41* @const42*/43goog.fx.anim.TIMEOUT = goog.async.AnimationDelay.TIMEOUT;444546/**47* A map of animations which should be cycled on the global timer.48*49* @type {!Object<number, goog.fx.anim.Animated>}50* @private51*/52goog.fx.anim.activeAnimations_ = {};535455/**56* An optional animation window.57* @type {?Window}58* @private59*/60goog.fx.anim.animationWindow_ = null;616263/**64* An interval ID for the global timer or event handler uid.65* @type {?goog.async.Delay|?goog.async.AnimationDelay}66* @private67*/68goog.fx.anim.animationDelay_ = null;697071/**72* Registers an animation to be cycled on the global timer.73* @param {goog.fx.anim.Animated} animation The animation to register.74*/75goog.fx.anim.registerAnimation = function(animation) {76'use strict';77var uid = goog.getUid(animation);78if (!(uid in goog.fx.anim.activeAnimations_)) {79goog.fx.anim.activeAnimations_[uid] = animation;80}8182// If the timer is not already started, start it now.83goog.fx.anim.requestAnimationFrame_();84};858687/**88* Removes an animation from the list of animations which are cycled on the89* global timer.90* @param {goog.fx.anim.Animated} animation The animation to unregister.91*/92goog.fx.anim.unregisterAnimation = function(animation) {93'use strict';94var uid = goog.getUid(animation);95delete goog.fx.anim.activeAnimations_[uid];9697// If a timer is running and we no longer have any active timers we stop the98// timers.99if (goog.object.isEmpty(goog.fx.anim.activeAnimations_)) {100goog.fx.anim.cancelAnimationFrame_();101}102};103104105/**106* Tears down this module. Useful for testing.107*/108// TODO(nicksantos): Wow, this api is pretty broken. This should be fixed.109goog.fx.anim.tearDown = function() {110'use strict';111goog.fx.anim.animationWindow_ = null;112goog.dispose(goog.fx.anim.animationDelay_);113goog.fx.anim.animationDelay_ = null;114goog.fx.anim.activeAnimations_ = {};115};116117118/**119* Registers an animation window. This allows usage of the timing control API120* for animations. Note that this window must be visible, as non-visible121* windows can potentially stop animating. This window does not necessarily122* need to be the window inside which animation occurs, but must remain visible.123* See: https://developer.mozilla.org/en/DOM/window.mozRequestAnimationFrame.124*125* @param {Window} animationWindow The window in which to animate elements.126*/127goog.fx.anim.setAnimationWindow = function(animationWindow) {128'use strict';129// If a timer is currently running, reset it and restart with new functions130// after a timeout. This is to avoid mismatching timer UIDs if we change the131// animation window during a running animation.132//133// In practice this cannot happen before some animation window and timer134// control functions has already been set.135var hasTimer =136goog.fx.anim.animationDelay_ && goog.fx.anim.animationDelay_.isActive();137138goog.dispose(goog.fx.anim.animationDelay_);139goog.fx.anim.animationDelay_ = null;140goog.fx.anim.animationWindow_ = animationWindow;141142// If the timer was running, start it again.143if (hasTimer) {144goog.fx.anim.requestAnimationFrame_();145}146};147148149/**150* Requests an animation frame based on the requestAnimationFrame and151* cancelRequestAnimationFrame function pair.152* @private153*/154goog.fx.anim.requestAnimationFrame_ = function() {155'use strict';156if (!goog.fx.anim.animationDelay_) {157// We cannot guarantee that the global window will be one that fires158// requestAnimationFrame events (consider off-screen chrome extension159// windows). Default to use goog.async.Delay, unless160// the client has explicitly set an animation window.161if (goog.fx.anim.animationWindow_) {162// requestAnimationFrame will call cycleAnimations_ with the current163// time in ms, as returned from goog.now().164goog.fx.anim.animationDelay_ =165new goog.async.AnimationDelay(function(now) {166'use strict';167goog.fx.anim.cycleAnimations_(now);168}, goog.fx.anim.animationWindow_);169} else {170goog.fx.anim.animationDelay_ = new goog.async.Delay(function() {171'use strict';172goog.fx.anim.cycleAnimations_(goog.now());173}, goog.fx.anim.TIMEOUT);174}175}176177var delay = goog.fx.anim.animationDelay_;178if (!delay.isActive()) {179delay.start();180}181};182183184/**185* Cancels an animation frame created by requestAnimationFrame_().186* @private187*/188goog.fx.anim.cancelAnimationFrame_ = function() {189'use strict';190if (goog.fx.anim.animationDelay_) {191goog.fx.anim.animationDelay_.stop();192}193};194195196/**197* Cycles through all registered animations.198* @param {number} now Current time in milliseconds.199* @private200*/201goog.fx.anim.cycleAnimations_ = function(now) {202'use strict';203goog.object.forEach(goog.fx.anim.activeAnimations_, function(anim) {204'use strict';205anim.onAnimationFrame(now);206});207208if (!goog.object.isEmpty(goog.fx.anim.activeAnimations_)) {209goog.fx.anim.requestAnimationFrame_();210}211};212213214