diff options
Diffstat (limited to 'catapult/third_party/polymer/components/web-animations-js/src/web-animations-next-animation.js')
-rw-r--r-- | catapult/third_party/polymer/components/web-animations-js/src/web-animations-next-animation.js | 383 |
1 files changed, 383 insertions, 0 deletions
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/web-animations-next-animation.js b/catapult/third_party/polymer/components/web-animations-js/src/web-animations-next-animation.js new file mode 100644 index 00000000..698532cd --- /dev/null +++ b/catapult/third_party/polymer/components/web-animations-js/src/web-animations-next-animation.js @@ -0,0 +1,383 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +(function(shared, scope, testing) { + scope.animationsWithPromises = []; + + scope.Animation = function(effect, timeline) { + this.id = ''; + if (effect && effect._id) { + this.id = effect._id; + } + this.effect = effect; + if (effect) { + effect._animation = this; + } + if (!timeline) { + throw new Error('Animation with null timeline is not supported'); + } + this._timeline = timeline; + this._sequenceNumber = shared.sequenceNumber++; + this._holdTime = 0; + this._paused = false; + this._isGroup = false; + this._animation = null; + this._childAnimations = []; + this._callback = null; + this._oldPlayState = 'idle'; + this._rebuildUnderlyingAnimation(); + // Animations are constructed in the idle state. + this._animation.cancel(); + this._updatePromises(); + }; + + scope.Animation.prototype = { + _updatePromises: function() { + var oldPlayState = this._oldPlayState; + var newPlayState = this.playState; + if (this._readyPromise && newPlayState !== oldPlayState) { + if (newPlayState == 'idle') { + this._rejectReadyPromise(); + this._readyPromise = undefined; + } else if (oldPlayState == 'pending') { + this._resolveReadyPromise(); + } else if (newPlayState == 'pending') { + this._readyPromise = undefined; + } + } + if (this._finishedPromise && newPlayState !== oldPlayState) { + if (newPlayState == 'idle') { + this._rejectFinishedPromise(); + this._finishedPromise = undefined; + } else if (newPlayState == 'finished') { + this._resolveFinishedPromise(); + } else if (oldPlayState == 'finished') { + this._finishedPromise = undefined; + } + } + this._oldPlayState = this.playState; + return (this._readyPromise || this._finishedPromise); + }, + _rebuildUnderlyingAnimation: function() { + this._updatePromises(); + var oldPlaybackRate; + var oldPaused; + var oldStartTime; + var oldCurrentTime; + var hadUnderlying = this._animation ? true : false; + if (hadUnderlying) { + oldPlaybackRate = this.playbackRate; + oldPaused = this._paused; + oldStartTime = this.startTime; + oldCurrentTime = this.currentTime; + this._animation.cancel(); + this._animation._wrapper = null; + this._animation = null; + } + + if (!this.effect || this.effect instanceof window.KeyframeEffect) { + this._animation = scope.newUnderlyingAnimationForKeyframeEffect(this.effect); + scope.bindAnimationForKeyframeEffect(this); + } + if (this.effect instanceof window.SequenceEffect || this.effect instanceof window.GroupEffect) { + this._animation = scope.newUnderlyingAnimationForGroup(this.effect); + scope.bindAnimationForGroup(this); + } + if (this.effect && this.effect._onsample) { + scope.bindAnimationForCustomEffect(this); + } + if (hadUnderlying) { + if (oldPlaybackRate != 1) { + this.playbackRate = oldPlaybackRate; + } + if (oldStartTime !== null) { + this.startTime = oldStartTime; + } else if (oldCurrentTime !== null) { + this.currentTime = oldCurrentTime; + } else if (this._holdTime !== null) { + this.currentTime = this._holdTime; + } + if (oldPaused) { + this.pause(); + } + } + this._updatePromises(); + }, + _updateChildren: function() { + if (!this.effect || this.playState == 'idle') + return; + + var offset = this.effect._timing.delay; + this._childAnimations.forEach(function(childAnimation) { + this._arrangeChildren(childAnimation, offset); + if (this.effect instanceof window.SequenceEffect) + offset += scope.groupChildDuration(childAnimation.effect); + }.bind(this)); + }, + _setExternalAnimation: function(animation) { + if (!this.effect || !this._isGroup) + return; + for (var i = 0; i < this.effect.children.length; i++) { + this.effect.children[i]._animation = animation; + this._childAnimations[i]._setExternalAnimation(animation); + } + }, + _constructChildAnimations: function() { + if (!this.effect || !this._isGroup) + return; + var offset = this.effect._timing.delay; + this._removeChildAnimations(); + this.effect.children.forEach(function(child) { + var childAnimation = scope.timeline._play(child); + this._childAnimations.push(childAnimation); + childAnimation.playbackRate = this.playbackRate; + if (this._paused) + childAnimation.pause(); + child._animation = this.effect._animation; + + this._arrangeChildren(childAnimation, offset); + + if (this.effect instanceof window.SequenceEffect) + offset += scope.groupChildDuration(child); + }.bind(this)); + }, + _arrangeChildren: function(childAnimation, offset) { + if (this.startTime === null) { + childAnimation.currentTime = this.currentTime - offset / this.playbackRate; + } else if (childAnimation.startTime !== this.startTime + offset / this.playbackRate) { + childAnimation.startTime = this.startTime + offset / this.playbackRate; + } + }, + get timeline() { + return this._timeline; + }, + get playState() { + return this._animation ? this._animation.playState : 'idle'; + }, + get finished() { + if (!window.Promise) { + console.warn('Animation Promises require JavaScript Promise constructor'); + return null; + } + if (!this._finishedPromise) { + if (scope.animationsWithPromises.indexOf(this) == -1) { + scope.animationsWithPromises.push(this); + } + this._finishedPromise = new Promise( + function(resolve, reject) { + this._resolveFinishedPromise = function() { + resolve(this); + }; + this._rejectFinishedPromise = function() { + reject({type: DOMException.ABORT_ERR, name: 'AbortError'}); + }; + }.bind(this)); + if (this.playState == 'finished') { + this._resolveFinishedPromise(); + } + } + return this._finishedPromise; + }, + get ready() { + if (!window.Promise) { + console.warn('Animation Promises require JavaScript Promise constructor'); + return null; + } + if (!this._readyPromise) { + if (scope.animationsWithPromises.indexOf(this) == -1) { + scope.animationsWithPromises.push(this); + } + this._readyPromise = new Promise( + function(resolve, reject) { + this._resolveReadyPromise = function() { + resolve(this); + }; + this._rejectReadyPromise = function() { + reject({type: DOMException.ABORT_ERR, name: 'AbortError'}); + }; + }.bind(this)); + if (this.playState !== 'pending') { + this._resolveReadyPromise(); + } + } + return this._readyPromise; + }, + get onfinish() { + return this._animation.onfinish; + }, + set onfinish(v) { + if (typeof v == 'function') { + this._animation.onfinish = (function(e) { + e.target = this; + v.call(this, e); + }).bind(this); + } else { + this._animation.onfinish = v; + } + }, + get oncancel() { + return this._animation.oncancel; + }, + set oncancel(v) { + if (typeof v == 'function') { + this._animation.oncancel = (function(e) { + e.target = this; + v.call(this, e); + }).bind(this); + } else { + this._animation.oncancel = v; + } + }, + get currentTime() { + this._updatePromises(); + var currentTime = this._animation.currentTime; + this._updatePromises(); + return currentTime; + }, + set currentTime(v) { + this._updatePromises(); + this._animation.currentTime = isFinite(v) ? v : Math.sign(v) * Number.MAX_VALUE; + this._register(); + this._forEachChild(function(child, offset) { + child.currentTime = v - offset; + }); + this._updatePromises(); + }, + get startTime() { + return this._animation.startTime; + }, + set startTime(v) { + this._updatePromises(); + this._animation.startTime = isFinite(v) ? v : Math.sign(v) * Number.MAX_VALUE; + this._register(); + this._forEachChild(function(child, offset) { + child.startTime = v + offset; + }); + this._updatePromises(); + }, + get playbackRate() { + return this._animation.playbackRate; + }, + set playbackRate(value) { + this._updatePromises(); + var oldCurrentTime = this.currentTime; + this._animation.playbackRate = value; + this._forEachChild(function(childAnimation) { + childAnimation.playbackRate = value; + }); + if (oldCurrentTime !== null) { + this.currentTime = oldCurrentTime; + } + this._updatePromises(); + }, + play: function() { + this._updatePromises(); + this._paused = false; + this._animation.play(); + if (this._timeline._animations.indexOf(this) == -1) { + this._timeline._animations.push(this); + } + this._register(); + scope.awaitStartTime(this); + this._forEachChild(function(child) { + var time = child.currentTime; + child.play(); + child.currentTime = time; + }); + this._updatePromises(); + }, + pause: function() { + this._updatePromises(); + if (this.currentTime) { + this._holdTime = this.currentTime; + } + this._animation.pause(); + this._register(); + this._forEachChild(function(child) { + child.pause(); + }); + this._paused = true; + this._updatePromises(); + }, + finish: function() { + this._updatePromises(); + this._animation.finish(); + this._register(); + this._updatePromises(); + }, + cancel: function() { + this._updatePromises(); + this._animation.cancel(); + this._register(); + this._removeChildAnimations(); + this._updatePromises(); + }, + reverse: function() { + this._updatePromises(); + var oldCurrentTime = this.currentTime; + this._animation.reverse(); + this._forEachChild(function(childAnimation) { + childAnimation.reverse(); + }); + if (oldCurrentTime !== null) { + this.currentTime = oldCurrentTime; + } + this._updatePromises(); + }, + addEventListener: function(type, handler) { + var wrapped = handler; + if (typeof handler == 'function') { + wrapped = (function(e) { + e.target = this; + handler.call(this, e); + }).bind(this); + handler._wrapper = wrapped; + } + this._animation.addEventListener(type, wrapped); + }, + removeEventListener: function(type, handler) { + this._animation.removeEventListener(type, (handler && handler._wrapper) || handler); + }, + _removeChildAnimations: function() { + while (this._childAnimations.length) + this._childAnimations.pop().cancel(); + }, + _forEachChild: function(f) { + var offset = 0; + if (this.effect.children && this._childAnimations.length < this.effect.children.length) + this._constructChildAnimations(); + this._childAnimations.forEach(function(child) { + f.call(this, child, offset); + if (this.effect instanceof window.SequenceEffect) + offset += child.effect.activeDuration; + }.bind(this)); + + if (this.playState == 'pending') + return; + var timing = this.effect._timing; + var t = this.currentTime; + if (t !== null) + t = shared.calculateIterationProgress(shared.calculateActiveDuration(timing), t, timing); + if (t == null || isNaN(t)) + this._removeChildAnimations(); + }, + }; + + window.Animation = scope.Animation; + + if (WEB_ANIMATIONS_TESTING) { + testing.webAnimationsNextAnimation = scope.Animation; + } + +})(webAnimationsShared, webAnimationsNext, webAnimationsTesting); |