-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathchoreographer.js
156 lines (140 loc) · 5.46 KB
/
choreographer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/**
* Choreographer
* =============
* A JavaScript library for orchestrating complex sequences of browser animations.
* Copyright (C) 2015 Aaron Greenberg
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
(function (context) {
/**
* Public | Choreographer
* ============================================================================
* An object for handling/synchronizing lots of animations. It was designed to
* choreograph long-running animations based around CSS transitions and
* animations, but it would likely also work for JavaScript-based animations.
*
* It works by storing a map from times (in milliseconds) to (arrays of)
* animation functions. Once the perform() method is invoked, the Choreographer
* sets up a timeout for each time in the map, and it sequentially runs all the
* functions stored for that time.
*
* Example
* -------
* If the choreography object were
*
* {
* 0: [ fn1 ],
* 300: [ fn2, fn3 ],
* 600: [ fn4 ]
* }
*
* then on invocation of Choreographer.perform(), three timeouts would be
* created:
*
* setTimeout(function () { fn1(); }, 0);
* setTimeout(function () { fn2(); fn3(); }, 300);
* setTimeout(function () { fn4(); }, 600);
*
* This allows each animation function to run in order. Presumably, the time
* interval between fn1 and fn2/fn3 corresponds to the length the animation
* function fn1 takes to complete.
*
* Properties
* ----------
* - duration [number] -> The default length of a single, given animation. This
* can be changed when you're adding an animation. If
* not passed in the constructor, defaults to 300.
*
* - time [number] -> The time at with the animations start. Defaults to 0.
*
* - choreography [object] -> A map from times to arrays of functions. The
* times represent the times at which the functions
* in the array will be called.
*/
function Choreographer(duration) {
this.duration = duration || 300;
this.time = 0;
this.choreography = Object.create(null);
}
Choreographer.prototype = Object.create(Object.prototype);
Choreographer.prototype.constructor = Choreographer;
/**
* Public | add()
* ============================================================================
* Adds a function to the Choreographer.choreography object at a certain time,
* thus scheduling it to be run at that time. Each time Choreographer.add() is
* called, a function is pushed onto the array stored at the current
* Choreographer.time key in the Choreographer.choreography object. Then, the
* Choreographer.time property is incremented by the passed duration, so the
* next array of functions won't be called until the current array has
* finished.
*
* Arguments
* ---------
* - fn [function] -> A function to be executed at the choreographed time.
*
* - duration [number] -> The length of time (in milliseconds) that the passed
* function will take to run.
*/
Choreographer.prototype.add = function (fn, duration) {
if (!fn || typeof fn !== 'function') {
throw new Error("No function was provided to Choreographer.add()");
}
// If there is already a function array at the current time, push the added
// function onto the array. Otherwise, create the array.
if (this.choreography[this.time]) {
(this.choreography[this.time]).push(fn);
} else {
this.choreography[this.time] = [ fn ];
}
if ((!duration || typeof duration !== 'number') && duration !== 0) {
var duration = this.duration;
}
this.time += duration;
}
/**
* Public | pause()
* ============================================================================
* Pauses the execution of functions for a given duration. Works by simply
* incrementing the Choreographer.time property, so the next function array
* will not be called for another `duration` milliseconds.
*
* Arguments
* ---------
* - duration [number] -> A length of time (in milliseconds) to pause.
*/
Choreographer.prototype.pause = function (duration) {
if (!duration || typeof duration !== 'number') {
var duration = this.duration;
}
this.time += duration;
}
/**
* Public | perform()
* ============================================================================
* Executes the functions stored in Choreographer.choreography by creating
* timeouts for each function and time.
*/
Choreographer.prototype.perform = function () {
for (var time in this.choreography) {
var taskList = this.choreography[time];
taskList.forEach(function (fn) {
setTimeout(fn, time);
});
}
}
context.Choreographer = Choreographer;
})(this);