1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
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 |
|
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 |
|
97 |
|
98 |
|
99 |
|
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 |
|
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);
|