var Class = require('../utils/Class'); var GetValue = require('../utils/object/GetValue'); var NOOP = require('../utils/NOOP'); var RequestAnimationFrame = require('../dom/RequestAnimationFrame'); var TimeStep = new Class({ initialize: function TimeStep(game, config){ this.game = game; this.raf = new RequestAnimationFrame(); this.started = false ; this.running = false ; this.minFps = GetValue(config, 'min', 5); this.targetFps = GetValue(config, 'target', 60); this._min = 1000 / this.minFps; this._target = 1000 / this.targetFps; this.actualFps = this.targetFps; this.nextFpsUpdate = 0; this.framesThisSecond = 0; this.callback = NOOP; this.forceSetTimeOut = GetValue(config, 'forceSetTimeOut', false ); this.time = 0; this.startTime = 0; this.lastTime = 0; this.frame = 0; this.inFocus = true ; this._pauseTime = 0; this._coolDown = 0; this.delta = 0; this.deltaIndex = 0; this.deltaHistory = [] ; this.deltaSmoothingMax = GetValue(config, 'deltaHistory', 10); this.panicMax = GetValue(config, 'panicMax', 120); this.rawDelta = 0; this.now = 0; this.smoothStep = GetValue(config, 'smoothStep', true ); } , blur: function (){ this.inFocus = false ; } , focus: function (){ this.inFocus = true ; this.resetDelta(); } , pause: function (){ this._pauseTime = window.performance.now(); } , resume: function (){ this.resetDelta(); this.startTime += this.time - this._pauseTime; } , resetDelta: function (){ var now = window.performance.now(); this.time = now; this.lastTime = now; this.nextFpsUpdate = now + 1000; this.framesThisSecond = 0; for (var i = 0; i < this.deltaSmoothingMax; i++ ){ this.deltaHistory[i] = Math.min(this._target, this.deltaHistory[i]); } this.delta = 0; this.deltaIndex = 0; this._coolDown = this.panicMax; } , start: function (callback){ if (this.started) { return this; } this.started = true ; this.running = true ; for (var i = 0; i < this.deltaSmoothingMax; i++ ){ this.deltaHistory[i] = this._target; } this.resetDelta(); this.startTime = window.performance.now(); this.callback = callback; this.raf.start(this.step.bind(this), this.forceSetTimeOut, this._target); } , step: function (){ var time = window.performance.now(); this.now = time; var before = time - this.lastTime; if (before < 0) { before = 0; } this.rawDelta = before; var idx = this.deltaIndex; var history = this.deltaHistory; var max = this.deltaSmoothingMax; var dt = before; var avg = before; if (this.smoothStep) { if (this._coolDown > 0 || !this.inFocus) { this._coolDown-- ; dt = Math.min(dt, this._target); } if (dt > this._min) { dt = history[idx]; dt = Math.min(dt, this._min); } history[idx] = dt; this.deltaIndex++ ; if (this.deltaIndex > max) { this.deltaIndex = 0; } avg = 0; for (var i = 0; i < max; i++ ){ avg += history[i]; } avg /= max; } this.delta = avg; this.time += this.rawDelta; if (time > this.nextFpsUpdate) { this.actualFps = 0.25 * this.framesThisSecond + 0.75 * this.actualFps; this.nextFpsUpdate = time + 1000; this.framesThisSecond = 0; } this.framesThisSecond++ ; var interpolation = avg / this._target; this.callback(time, avg, interpolation); this.lastTime = time; this.frame++ ; } , tick: function (){ this.step(); } , sleep: function (){ if (this.running) { this.raf.stop(); this.running = false ; } } , wake: function (seamless){ if (this.running) { return ; } else if (seamless) { this.startTime += - this.lastTime + (this.lastTime + window.performance.now()); } this.raf.start(this.step.bind(this), this.useRAF); this.running = true ; this.step(); } , getDuration: function (){ return Math.round(this.lastTime - this.startTime) / 1000; } , getDurationMS: function (){ return Math.round(this.lastTime - this.startTime); } , stop: function (){ this.running = false ; this.started = false ; this.raf.stop(); return this; } , destroy: function (){ this.stop(); this.callback = NOOP; this.raf = null ; this.game = null ; } } ); module.exports = TimeStep;