Skip to content

Commit 802e41c

Browse files
committed
make it more flexible for child class to override parent functionality so that it can take advantage of the interval feature. Also make a way to force a loop to tick once
1 parent 3c3cf00 commit 802e41c

File tree

1 file changed

+49
-42
lines changed

1 file changed

+49
-42
lines changed

src/index.js

Lines changed: 49 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ class AnimationLoop {
1515
self.animationFns = new Set()
1616
self.animationFnsAfter = new Set()
1717
self.baseFns = new Set()
18+
1819
self.animationFrame = null
20+
self.needsToRequestEachFrame = true
1921

2022
self.elapsed = 0
2123
self.lastTick = 0
@@ -26,15 +28,22 @@ class AnimationLoop {
2628
self.started = false
2729
self.paused = false
2830
self.ticking = false
31+
self.forcedTick = false
2932

3033
self.childLoops = new Set()
34+
35+
this._tick = this._tick.bind(this)
3136
}
3237

3338
// read-only
3439
get elapsed() { return _(this).elapsed }
3540
get started() { return _(this).started }
3641
get paused() { return _(this).paused }
3742
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 }
3847

3948
addAnimationFnBefore(fn) {
4049
const self = _(this)
@@ -79,13 +88,6 @@ class AnimationLoop {
7988
self.animationFnsAfter.size
8089
}
8190

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.
8991
addBaseFn(fn) {
9092
const self = _(this)
9193
if (typeof fn === 'function') self.baseFns.add(fn)
@@ -140,45 +142,57 @@ class AnimationLoop {
140142
if ( self.ticking ) return
141143

142144
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 )
151146
}
152147

153148
_stopTicking() {
154149
const self = _(this)
155150
self.ticking = false
156-
cancelAnimationFrame(self.animationFrame)
151+
this._cancelFrame(self.animationFrame)
157152
}
158153

159-
_tick( dt ) {
154+
_requestFrame( fn ) {
155+
return requestAnimationFrame( fn )
156+
}
157+
158+
_cancelFrame( fn ) {
159+
cancelAnimationFrame( fn )
160+
}
161+
162+
_tick() {
160163
const self = _(this)
161164

165+
let dt = self.clock.getDelta()
166+
162167
self.elapsed += dt
163168

164169
if ( !self.interval ) {
165170

166171
this._callAnimationFunctions( dt )
167-
return
168172

169173
}
170174

171-
const numIntervals = Math.floor( self.elapsed / self.interval )
175+
else {
172176

173-
if ( numIntervals > self.intervals ) {
177+
const numIntervals = Math.floor( self.elapsed / self.interval )
174178

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+
}
179188

180189
}
181190

191+
if ( self.needsToRequestEachFrame )
192+
self.animationFrame = this._requestFrame( this._tick )
193+
194+
if ( !this.hasAnimationFunctions() ) this._stopTicking()
195+
182196
}
183197

184198
_callAnimationFunctions( dt ) {
@@ -197,13 +211,13 @@ class AnimationLoop {
197211
if ( fn(dt, self.elapsed) === false ) this.removeBaseFn( fn )
198212
}
199213

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.
203216
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)
207221
}
208222

209223
addChildLoop( child ) {
@@ -230,24 +244,17 @@ class ChildAnimationLoop extends AnimationLoop {
230244
super()
231245
const self = _(this)
232246
self.parentLoop = null
247+
self.needsToRequestEachFrame = false
233248
}
234249

235-
_startTicking() {
250+
_requestFrame( fn ) {
236251
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 )
246253
}
247254

248-
_stopTicking() {
255+
_cancelFrame( fn ) {
249256
const self = _(this)
250-
self.parentLoop.removeAnimationFn( self.animationFrame )
257+
self.parentLoop.removeAnimationFn( fn )
251258
}
252259
}
253260

0 commit comments

Comments
 (0)