• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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    tick(t);
41    processing.forEach(function(entry) { entry[1](t); });
42    if (needsRetick)
43      tick(t);
44    applyPendingEffects();
45  }
46
47  function comparePlayers(leftPlayer, rightPlayer) {
48    return leftPlayer._sequenceNumber - rightPlayer._sequenceNumber;
49  }
50
51  function InternalTimeline() {
52    this._players = [];
53    // Android 4.3 browser has window.performance, but not window.performance.now
54    this.currentTime = window.performance && performance.now ? performance.now() : 0;
55  };
56
57  InternalTimeline.prototype = {
58    _play: function(source) {
59      source._timing = shared.normalizeTimingInput(source.timing);
60      var player = new scope.Player(source);
61      player._idle = false;
62      player._timeline = this;
63      this._players.push(player);
64      scope.restart();
65      scope.invalidateEffects();
66      return player;
67    }
68  };
69
70  var ticking = false;
71  var hasRestartedThisFrame = false;
72
73  scope.restart = function() {
74    if (!ticking) {
75      ticking = true;
76      requestAnimationFrame(function() {});
77      hasRestartedThisFrame = true;
78    }
79    return hasRestartedThisFrame;
80  };
81
82  var needsRetick = false;
83  scope.invalidateEffects = function() {
84    needsRetick = true;
85  };
86
87  var pendingEffects = [];
88  function applyPendingEffects() {
89    pendingEffects.forEach(function(f) { f(); });
90    pendingEffects.length = 0;
91  }
92
93  var originalGetComputedStyle = window.getComputedStyle;
94  Object.defineProperty(window, 'getComputedStyle', {
95    configurable: true,
96    enumerable: true,
97    value: function() {
98      if (needsRetick) tick(timeline.currentTime);
99      applyPendingEffects();
100      return originalGetComputedStyle.apply(this, arguments);
101    },
102  });
103
104  function tick(t) {
105    hasRestartedThisFrame = false;
106    var timeline = scope.timeline;
107    timeline.currentTime = t;
108    timeline._players.sort(comparePlayers);
109    ticking = false;
110    var updatingPlayers = timeline._players;
111    timeline._players = [];
112
113    var newPendingClears = [];
114    var newPendingEffects = [];
115    updatingPlayers = updatingPlayers.filter(function(player) {
116      player._inTimeline = player._tick(t);
117
118      if (!player._inEffect)
119        newPendingClears.push(player._source);
120      else
121        newPendingEffects.push(player._source);
122
123      if (!player.finished && !player.paused && !player._idle)
124        ticking = true;
125
126      return player._inTimeline;
127    });
128
129    // FIXME: Should remove dupliactes from pendingEffects.
130    pendingEffects.push.apply(pendingEffects, newPendingClears);
131    pendingEffects.push.apply(pendingEffects, newPendingEffects);
132
133    timeline._players.push.apply(timeline._players, updatingPlayers);
134    needsRetick = false;
135
136    if (ticking)
137      requestAnimationFrame(function() {});
138  };
139
140  if (WEB_ANIMATIONS_TESTING) {
141    testing.tick = processRafCallbacks;
142    testing.isTicking = function() { return ticking; };
143    testing.setTicking = function(newVal) { ticking = newVal; };
144  }
145
146  var timeline = new InternalTimeline();
147  scope.timeline = timeline;
148
149})(webAnimationsShared, webAnimations1, webAnimationsTesting);
150