UNPKG

jsdk-offical

Version:

JSDK is the most comprehensive TypeScript framework, like JDK.

241 lines (211 loc) 7.73 kB
/** * @project JSDK * @license MIT * @website https://github.com/fengboyue/jsdk * * @version 2.1.0 * @date 2020/7/1 * @author Frank.Feng * @update Realized constant interval time for constant FPS, which be named as "OF" mode. * * @version 2.0.0 * @author Frank.Feng */ module JS { export namespace util { export type TimerTask = (this: Timer, elapsedTime: number) => void; export type TimerInit = { /** * Delays (ms) the start of Timer. */ delay?: number; /** * Defines max loop of task.<br> * True means is Infinity.<br> * False means is 0.<br> * <br> * The default value is 1. */ loop?: boolean | number, /** * millisecond. */ interval?: number, /** * BF means the interval time which is between every task.<br> * OF means the interval time which is occupied by every task. <br> * If you want to keep in a constant task rate, you should select OF.<br> * Note: The timer may skip some executions of the task in OF mode when the execution time exceeded the interval time. * <br> * The default mode is BF that is safe. */ intervalMode?: 'OF' | 'BF' } export type TimerEvents = 'starting' | 'finished' | 'looping' | 'looped' | 'pausing' | 'paused'; export enum TimerState { STOPPED, RUNNING, PAUSED } export class Timer { protected _bus = new EventBus(this); protected _cfg: TimerInit; protected _task: TimerTask; protected _timer; protected _sta: TimerState = TimerState.STOPPED; protected _ts0; //save timestamp at starting protected _ts: number = 0; //timestamp of prev task protected _et: number = 0; //elapsed time of prev task protected _pt: number = 0; //pause time protected _count = 0; constructor(task: TimerTask, cfg?: TimerInit) { this._task = task; this._config(cfg); } on(type: TimerEvents, fn: EventHandler<this>) { this._bus.on(type, fn); return this } off(type: TimerEvents, fn?: EventHandler<this>) { this._bus.off(type, fn); return this } count() { return this._count } private _config(cfg: TimerInit): any { this._cfg = Jsons.union(<TimerInit>{ delay: 0, loop: 1, interval: 0, intervalMode: 'BF' }, this._cfg, cfg); let c = this._cfg; if (c.interval != void 0 && c.interval < 0) c.interval = 0; let l = c.loop; l = l == false || l < 0 ? 0 : (l === true ? Infinity : l); c.loop = l; return this } pause() { let m = this; if (!m.isRunning()) return m; m._bus.fire(<TimerEvents>'pausing', [m._count + 1]); m._state(TimerState.PAUSED); m._pt = System.highResTime();//record pause time m._bus.fire(<TimerEvents>'paused', [m._count + 1]); return m } protected _cancelTimer() { if (this._timer) window.clearTimeout(this._timer); this._timer = null; } protected _reset() { let m = this; m._cancelTimer(); m._state(TimerState.STOPPED); m._count = 0; m._ts0 = 0; m._ts = 0; m._et = 0; m._pt = 0; } stop() { this._finish(); return this } protected _finish() { this._reset(); this._bus.fire(<TimerEvents>'finished'); } /** * Returns state of the timer */ getState(): TimerState { return this._sta } isRunning(): boolean { return this.getState() == TimerState.RUNNING } protected _state(s: TimerState) { this._sta = s } /** * Returns current frame rate. */ fps() { return this._et == 0 ? 0 : 1000 / this._et } maxFPS() { let t = this._cfg.interval; return t == 0 ? Infinity : 1000 / t } looped() { return this._count+1 } private _loopTask(skip?: boolean) { if (!this.isRunning()) return;//暂停时停止执行此函数 let T = this, p = <number>T._cfg.loop; if (T._count < p) { let t = 0, opts = T._cfg, t0 = System.highResTime(); T._et = t0 - T._ts;//update frame time if (!skip) { T._bus.fire(<TimerEvents>'looping', [T._count + 1]); T._task.call(T, T._et); T._bus.fire(<TimerEvents>'looped', [T._count + 1]); let t1 = System.highResTime(); t = t1 - t0; ++T._count; } //update timestamp T._ts = t0; let d = opts.interval - t, needSkip = opts.intervalMode == 'OF' && d < 0; if(needSkip) { T._loopTask(needSkip) } else { T._timer = setTimeout( () => { T._loopTask(needSkip) }, opts.intervalMode == 'BF' ? opts.interval : d ) } } else { T._finish() } } protected _start(begin:boolean){ this._loopTask(!begin) } /** * Execute a ticker periodly. * 周期性执行任务 */ start() { let T = this; if (this.isRunning()) return; let first = false, wait = T._cfg.delay; if (T.getState() == TimerState.PAUSED) { wait = 0; //补上从暂停到现在的时间差 let dt = System.highResTime() - T._pt; T._ts0 += dt; T._ts += dt; T._pt = 0; } else { first = true; T._reset(); } T._state(TimerState.RUNNING); T._timer = setTimeout(() => { if (first) { this._ts0 = System.highResTime(); this._ts = this._ts0; T._bus.fire(<TimerEvents>'starting'); } T._start(first); }, wait); } } } } import Timer = JS.util.Timer; import TimerState = JS.util.TimerState; import TimerEvents = JS.util.TimerEvents; import TimerTask = JS.util.TimerTask; import TimerInit = JS.util.TimerInit;