@@ -15,7 +15,9 @@ class AnimationLoop {
15
15
self . animationFns = new Set ( )
16
16
self . animationFnsAfter = new Set ( )
17
17
self . baseFns = new Set ( )
18
+
18
19
self . animationFrame = null
20
+ self . needsToRequestEachFrame = true
19
21
20
22
self . elapsed = 0
21
23
self . lastTick = 0
@@ -26,15 +28,22 @@ class AnimationLoop {
26
28
self . started = false
27
29
self . paused = false
28
30
self . ticking = false
31
+ self . forcedTick = false
29
32
30
33
self . childLoops = new Set ( )
34
+
35
+ this . _tick = this . _tick . bind ( this )
31
36
}
32
37
33
38
// read-only
34
39
get elapsed ( ) { return _ ( this ) . elapsed }
35
40
get started ( ) { return _ ( this ) . started }
36
41
get paused ( ) { return _ ( this ) . paused }
37
42
get running ( ) { return _ ( this ) . started && ! _ ( this ) . paused }
43
+ get ticking ( ) { return _ ( this ) . ticking }
44
+
45
+ get interval ( ) { return _ ( this ) . interval }
46
+ set interval ( value ) { _ ( this ) . interval = value }
38
47
39
48
addAnimationFnBefore ( fn ) {
40
49
const self = _ ( this )
@@ -79,13 +88,6 @@ class AnimationLoop {
79
88
self . animationFnsAfter . size
80
89
}
81
90
82
- // base functions are executed after regular functions. They aren't repeated
83
- // on their own, they are only fired after animation functions. If there are
84
- // no animation functions, then base functions don't fire even if they remain
85
- // added to the AnimationLoop instance. This is useful for adding tasks that
86
- // always need to be triggered after updating animation values, for example
87
- // in Three.js we need to always call renderer.render(scene, camera) to
88
- // update the drawing.
89
91
addBaseFn ( fn ) {
90
92
const self = _ ( this )
91
93
if ( typeof fn === 'function' ) self . baseFns . add ( fn )
@@ -140,45 +142,57 @@ class AnimationLoop {
140
142
if ( self . ticking ) return
141
143
142
144
self . ticking = true
143
-
144
- const fn = ( ) => {
145
- this . _tick ( self . clock . getDelta ( ) )
146
- self . animationFrame = requestAnimationFrame ( fn )
147
- if ( ! this . hasAnimationFunctions ( ) ) this . _stopTicking ( )
148
- }
149
-
150
- self . animationFrame = requestAnimationFrame ( fn )
145
+ self . animationFrame = this . _requestFrame ( this . _tick )
151
146
}
152
147
153
148
_stopTicking ( ) {
154
149
const self = _ ( this )
155
150
self . ticking = false
156
- cancelAnimationFrame ( self . animationFrame )
151
+ this . _cancelFrame ( self . animationFrame )
157
152
}
158
153
159
- _tick ( dt ) {
154
+ _requestFrame ( fn ) {
155
+ return requestAnimationFrame ( fn )
156
+ }
157
+
158
+ _cancelFrame ( fn ) {
159
+ cancelAnimationFrame ( fn )
160
+ }
161
+
162
+ _tick ( ) {
160
163
const self = _ ( this )
161
164
165
+ let dt = self . clock . getDelta ( )
166
+
162
167
self . elapsed += dt
163
168
164
169
if ( ! self . interval ) {
165
170
166
171
this . _callAnimationFunctions ( dt )
167
- return
168
172
169
173
}
170
174
171
- const numIntervals = Math . floor ( self . elapsed / self . interval )
175
+ else {
172
176
173
- if ( numIntervals > self . intervals ) {
177
+ const numIntervals = Math . floor ( self . elapsed / self . interval )
174
178
175
- dt = self . elapsed - self . lastTick
176
- this . _callAnimationFunctions ( dt )
177
- self . intervals = numIntervals
178
- self . lastTick = self . elapsed
179
+ if ( numIntervals > self . intervals ) {
180
+
181
+ dt = self . elapsed - self . lastTick
182
+ self . intervals = numIntervals
183
+ self . lastTick = self . elapsed
184
+
185
+ this . _callAnimationFunctions ( dt )
186
+
187
+ }
179
188
180
189
}
181
190
191
+ if ( self . needsToRequestEachFrame )
192
+ self . animationFrame = this . _requestFrame ( this . _tick )
193
+
194
+ if ( ! this . hasAnimationFunctions ( ) ) this . _stopTicking ( )
195
+
182
196
}
183
197
184
198
_callAnimationFunctions ( dt ) {
@@ -197,13 +211,13 @@ class AnimationLoop {
197
211
if ( fn ( dt , self . elapsed ) === false ) this . removeBaseFn ( fn )
198
212
}
199
213
200
- get interval ( ) { return _ ( this ) . interval }
201
- set interval ( value ) { _ ( this ) . interval = value }
202
-
214
+ // add an empty function that removes itself on the next tick, forcing a
215
+ // tick of all other animation base functions.
203
216
forceTick ( ) {
204
- // add an empty function that removes itself on the next tick, forcing a
205
- // tick of all other animation base functions.
206
- this . addAnimationFn ( ( ) => false )
217
+ const self = _ ( this )
218
+ if ( self . forcedTick ) return
219
+ self . forcedTick = true
220
+ this . addAnimationFn ( ( ) => self . forcedTick = false )
207
221
}
208
222
209
223
addChildLoop ( child ) {
@@ -230,24 +244,17 @@ class ChildAnimationLoop extends AnimationLoop {
230
244
super ( )
231
245
const self = _ ( this )
232
246
self . parentLoop = null
247
+ self . needsToRequestEachFrame = false
233
248
}
234
249
235
- _startTicking ( ) {
250
+ _requestFrame ( fn ) {
236
251
const self = _ ( this )
237
-
238
- if ( ! self . parentLoop )
239
- throw new Error ( 'ChildAnimationLoop must have parent AnimationLoop before being started' )
240
-
241
- const fn = ( dt , elapsed ) => {
242
- this . _tick ( dt )
243
- }
244
-
245
- self . animationFrame = self . parentLoop . addAnimationFn ( fn )
252
+ return self . parentLoop . addAnimationFn ( fn )
246
253
}
247
254
248
- _stopTicking ( ) {
255
+ _cancelFrame ( fn ) {
249
256
const self = _ ( this )
250
- self . parentLoop . removeAnimationFn ( self . animationFrame )
257
+ self . parentLoop . removeAnimationFn ( fn )
251
258
}
252
259
}
253
260
0 commit comments