Spaces:
Running
Running
// Due to the existence of features such as interpolation and "0 FPS" being treated as "screen refresh rate", | |
// The VM loop logic has become much more complex | |
// Use setTimeout to polyfill requestAnimationFrame in Node.js environments | |
const _requestAnimationFrame = typeof requestAnimationFrame === 'function' ? | |
requestAnimationFrame : | |
(f => setTimeout(f, 1000 / 60)); | |
const _cancelAnimationFrame = typeof requestAnimationFrame === 'function' ? | |
cancelAnimationFrame : | |
clearTimeout; | |
const animationFrameWrapper = callback => { | |
let id; | |
const handle = () => { | |
id = _requestAnimationFrame(handle); | |
callback(); | |
}; | |
const cancel = () => _cancelAnimationFrame(id); | |
id = _requestAnimationFrame(handle); | |
return { | |
cancel | |
}; | |
}; | |
class FrameLoop { | |
constructor (runtime) { | |
this.runtime = runtime; | |
this.running = false; | |
this.setFramerate(30); | |
this.setInterpolation(false); | |
this.stepCallback = this.stepCallback.bind(this); | |
this.interpolationCallback = this.interpolationCallback.bind(this); | |
this._stepInterval = null; | |
this._interpolationAnimation = null; | |
this._stepAnimation = null; | |
this._stepCounter = 0; | |
} | |
setFramerate (fps) { | |
this.framerate = fps; | |
this._restart(); | |
} | |
setInterpolation (interpolation) { | |
this.interpolation = interpolation; | |
this._restart(); | |
} | |
stepCallback () { | |
this.runtime._step(); | |
} | |
interpolationCallback () { | |
this.runtime._renderInterpolatedPositions(); | |
} | |
_restart () { | |
if (this.running) { | |
this.stop(); | |
this.start(); | |
} | |
} | |
start () { | |
this.running = true; | |
if (this.framerate === 0) { | |
this._stepAnimation = animationFrameWrapper(this.stepCallback); | |
this.runtime.currentStepTime = 1000 / 60; | |
} else { | |
// Interpolation should never be enabled when framerate === 0 as that's just redundant | |
if (this.interpolation) { | |
this._interpolationAnimation = animationFrameWrapper(this.interpolationCallback); | |
} | |
this._stepInterval = setInterval(this.stepCallback, 1000 / this.framerate); | |
this.runtime.currentStepTime = 1000 / this.framerate; | |
} | |
} | |
stop () { | |
this.running = false; | |
clearInterval(this._stepInterval); | |
if (this._interpolationAnimation) { | |
this._interpolationAnimation.cancel(); | |
} | |
if (this._stepAnimation) { | |
this._stepAnimation.cancel(); | |
} | |
this._interpolationAnimation = null; | |
this._stepAnimation = null; | |
} | |
} | |
module.exports = FrameLoop; | |