1// Copyright 2014 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15(function(shared, scope, testing) { 16 17 function groupChildDuration(node) { 18 return node._timing.delay + node.activeDuration + node._timing.endDelay; 19 }; 20 21 function KeyframeEffect(effect) { 22 this._frames = shared.normalizeKeyframes(effect); 23 } 24 25 KeyframeEffect.prototype = { 26 getFrames: function() { return this._frames; } 27 }; 28 29 scope.Animation = function(target, effect, timingInput) { 30 this.target = target; 31 32 // TODO: Store a clone, not the same instance. 33 this._timingInput = timingInput; 34 this._timing = shared.normalizeTimingInput(timingInput); 35 36 // TODO: Make modifications to timing update the underlying player 37 this.timing = shared.makeTiming(timingInput); 38 // TODO: Make this a live object - will need to separate normalization of 39 // keyframes into a shared module. 40 if (typeof effect == 'function') 41 this.effect = effect; 42 else 43 this.effect = new KeyframeEffect(effect); 44 this._effect = effect; 45 this.activeDuration = shared.calculateActiveDuration(this._timing); 46 return this; 47 }; 48 49 var originalElementAnimate = Element.prototype.animate; 50 Element.prototype.animate = function(effect, timing) { 51 return scope.timeline.play(new scope.Animation(this, effect, timing)); 52 }; 53 54 var nullTarget = document.createElementNS('http://www.w3.org/1999/xhtml', 'div'); 55 scope.newUnderlyingPlayerForAnimation = function(animation) { 56 var target = animation.target || nullTarget; 57 var effect = animation._effect; 58 if (typeof effect == 'function') { 59 effect = []; 60 } 61 return originalElementAnimate.apply(target, [effect, animation._timingInput]); 62 }; 63 64 scope.bindPlayerForAnimation = function(player) { 65 if (player.source && typeof player.source.effect == 'function') { 66 scope.bindPlayerForCustomEffect(player); 67 } 68 }; 69 70 var pendingGroups = []; 71 scope.awaitStartTime = function(groupPlayer) { 72 if (groupPlayer.startTime !== null || !groupPlayer._isGroup) 73 return; 74 if (pendingGroups.length == 0) { 75 requestAnimationFrame(updatePendingGroups); 76 } 77 pendingGroups.push(groupPlayer); 78 }; 79 function updatePendingGroups() { 80 var updated = false; 81 while (pendingGroups.length) { 82 pendingGroups.shift()._updateChildren(); 83 updated = true; 84 } 85 return updated; 86 } 87 var originalGetComputedStyle = window.getComputedStyle; 88 Object.defineProperty(window, 'getComputedStyle', { 89 configurable: true, 90 enumerable: true, 91 value: function() { 92 var result = originalGetComputedStyle.apply(this, arguments); 93 if (updatePendingGroups()) 94 result = originalGetComputedStyle.apply(this, arguments); 95 return result; 96 }, 97 }); 98 99 // TODO: Call into this less frequently. 100 scope.Player.prototype._updateChildren = function() { 101 if (!this.source || !this._isGroup || this.playState == 'idle') 102 return; 103 var offset = this.source._timing.delay; 104 for (var i = 0; i < this.source.children.length; i++) { 105 var child = this.source.children[i]; 106 var childPlayer; 107 108 if (i >= this._childPlayers.length) { 109 childPlayer = window.document.timeline.play(child); 110 this._childPlayers.push(childPlayer); 111 childPlayer.playbackRate = this.playbackRate; 112 if (this.paused) { 113 childPlayer.pause(); 114 } 115 } else { 116 childPlayer = this._childPlayers[i]; 117 } 118 child.player = this.source.player; 119 120 if (childPlayer.startTime != this.startTime + offset) { 121 if (this.startTime === null) { 122 childPlayer.currentTime = this.source.player.currentTime - offset; 123 childPlayer._startTime = null; 124 } else { 125 childPlayer.startTime = this.startTime + offset; 126 } 127 childPlayer._updateChildren(); 128 } 129 130 if (this.playbackRate == -1 && this.currentTime < offset && childPlayer.currentTime !== -1) { 131 childPlayer.currentTime = -1; 132 } 133 134 if (this.source instanceof window.AnimationSequence) 135 offset += groupChildDuration(child); 136 } 137 }; 138 139 window.Animation = scope.Animation; 140 window.Element.prototype.getAnimationPlayers = function() { 141 return document.timeline.getAnimationPlayers().filter(function(player) { 142 return player.source !== null && player.source.target == this; 143 }.bind(this)); 144 }; 145 146 scope.groupChildDuration = groupChildDuration; 147 148}(webAnimationsShared, webAnimationsNext, webAnimationsTesting)); 149