OLD | NEW |
(Empty) | |
| 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 |
| 16 (function(shared, scope, testing) { |
| 17 var originalRequestAnimationFrame = window.requestAnimationFrame; |
| 18 var rafCallbacks = []; |
| 19 var rafId = 0; |
| 20 window.requestAnimationFrame = function(f) { |
| 21 var id = rafId++; |
| 22 if (rafCallbacks.length == 0 && !WEB_ANIMATIONS_TESTING) { |
| 23 originalRequestAnimationFrame(processRafCallbacks); |
| 24 } |
| 25 rafCallbacks.push([id, f]); |
| 26 return id; |
| 27 }; |
| 28 |
| 29 window.cancelAnimationFrame = function(id) { |
| 30 rafCallbacks.forEach(function(entry) { |
| 31 if (entry[0] == id) { |
| 32 entry[1] = function() {}; |
| 33 } |
| 34 }); |
| 35 }; |
| 36 |
| 37 function processRafCallbacks(t) { |
| 38 var processing = rafCallbacks; |
| 39 rafCallbacks = []; |
| 40 if (t < timeline.currentTime) |
| 41 t = timeline.currentTime; |
| 42 timeline._animations.sort(compareAnimations); |
| 43 timeline._animations = tick(t, true, timeline._animations)[0]; |
| 44 processing.forEach(function(entry) { entry[1](t); }); |
| 45 applyPendingEffects(); |
| 46 _now = undefined; |
| 47 } |
| 48 |
| 49 function compareAnimations(leftAnimation, rightAnimation) { |
| 50 return leftAnimation._sequenceNumber - rightAnimation._sequenceNumber; |
| 51 } |
| 52 |
| 53 function InternalTimeline() { |
| 54 this._animations = []; |
| 55 // Android 4.3 browser has window.performance, but not window.performance.no
w |
| 56 this.currentTime = window.performance && performance.now ? performance.now()
: 0; |
| 57 }; |
| 58 |
| 59 InternalTimeline.prototype = { |
| 60 _play: function(effect) { |
| 61 effect._timing = shared.normalizeTimingInput(effect.timing); |
| 62 var animation = new scope.Animation(effect); |
| 63 animation._idle = false; |
| 64 animation._timeline = this; |
| 65 this._animations.push(animation); |
| 66 scope.restart(); |
| 67 scope.applyDirtiedAnimation(animation); |
| 68 return animation; |
| 69 } |
| 70 }; |
| 71 |
| 72 var _now = undefined; |
| 73 |
| 74 if (WEB_ANIMATIONS_TESTING) { |
| 75 var now = function() { return timeline.currentTime; }; |
| 76 } else { |
| 77 var now = function() { |
| 78 if (_now == undefined) |
| 79 _now = window.performance && performance.now ? performance.now() : Date.
now(); |
| 80 return _now; |
| 81 }; |
| 82 } |
| 83 |
| 84 var ticking = false; |
| 85 var hasRestartedThisFrame = false; |
| 86 |
| 87 scope.restart = function() { |
| 88 if (!ticking) { |
| 89 ticking = true; |
| 90 requestAnimationFrame(function() {}); |
| 91 hasRestartedThisFrame = true; |
| 92 } |
| 93 return hasRestartedThisFrame; |
| 94 }; |
| 95 |
| 96 // RAF is supposed to be the last script to occur before frame rendering but n
ot |
| 97 // all browsers behave like this. This function is for synchonously updating a
n |
| 98 // animation's effects whenever its state is mutated by script to work around |
| 99 // incorrect script execution ordering by the browser. |
| 100 scope.applyDirtiedAnimation = function(animation) { |
| 101 if (inTick) { |
| 102 return; |
| 103 } |
| 104 animation._markTarget(); |
| 105 var animations = animation._targetAnimations(); |
| 106 animations.sort(compareAnimations); |
| 107 var inactiveAnimations = tick(scope.timeline.currentTime, false, animations.
slice())[1]; |
| 108 inactiveAnimations.forEach(function(animation) { |
| 109 var index = timeline._animations.indexOf(animation); |
| 110 if (index !== -1) { |
| 111 timeline._animations.splice(index, 1); |
| 112 } |
| 113 }); |
| 114 applyPendingEffects(); |
| 115 }; |
| 116 |
| 117 var pendingEffects = []; |
| 118 function applyPendingEffects() { |
| 119 pendingEffects.forEach(function(f) { f(); }); |
| 120 pendingEffects.length = 0; |
| 121 } |
| 122 |
| 123 var t60hz = 1000 / 60; |
| 124 |
| 125 var inTick = false; |
| 126 function tick(t, isAnimationFrame, updatingAnimations) { |
| 127 inTick = true; |
| 128 hasRestartedThisFrame = false; |
| 129 var timeline = scope.timeline; |
| 130 |
| 131 timeline.currentTime = t; |
| 132 ticking = false; |
| 133 |
| 134 var newPendingClears = []; |
| 135 var newPendingEffects = []; |
| 136 var activeAnimations = []; |
| 137 var inactiveAnimations = []; |
| 138 updatingAnimations.forEach(function(animation) { |
| 139 animation._tick(t, isAnimationFrame); |
| 140 |
| 141 if (!animation._inEffect) { |
| 142 newPendingClears.push(animation._effect); |
| 143 animation._unmarkTarget(); |
| 144 } else { |
| 145 newPendingEffects.push(animation._effect); |
| 146 animation._markTarget(); |
| 147 } |
| 148 |
| 149 if (animation._needsTick) |
| 150 ticking = true; |
| 151 |
| 152 var alive = animation._inEffect || animation._needsTick; |
| 153 animation._inTimeline = alive; |
| 154 if (alive) { |
| 155 activeAnimations.push(animation); |
| 156 } else { |
| 157 inactiveAnimations.push(animation); |
| 158 } |
| 159 }); |
| 160 |
| 161 // FIXME: Should remove dupliactes from pendingEffects. |
| 162 pendingEffects.push.apply(pendingEffects, newPendingClears); |
| 163 pendingEffects.push.apply(pendingEffects, newPendingEffects); |
| 164 |
| 165 if (ticking) |
| 166 requestAnimationFrame(function() {}); |
| 167 |
| 168 inTick = false; |
| 169 return [activeAnimations, inactiveAnimations]; |
| 170 }; |
| 171 |
| 172 if (WEB_ANIMATIONS_TESTING) { |
| 173 testing.tick = function(t) { timeline.currentTime = t; processRafCallbacks(t
); }; |
| 174 testing.isTicking = function() { return ticking; }; |
| 175 testing.setTicking = function(newVal) { ticking = newVal; }; |
| 176 } |
| 177 |
| 178 var timeline = new InternalTimeline(); |
| 179 scope.timeline = timeline; |
| 180 |
| 181 })(webAnimationsShared, webAnimations1, webAnimationsTesting); |
OLD | NEW |