@armniko/ticker
Advanced tools
| var s={d:(t,e)=>{for(var i in e)s.o(e,i)&&!s.o(t,i)&&Object.defineProperty(t,i,{enumerable:!0,get:e[i]})},o:(s,t)=>Object.prototype.hasOwnProperty.call(s,t)},t={};s.d(t,{R:()=>a});class e{_minFps;_currentFps=60;_lastChecks=[];_calculateFpsEachMs=2e3;_msSinceLastCalculation=0;_lowFpsInRow=0;_lowFpsCallback;currentFps(){return this._currentFps}calculate(s){this.collectFps(s),this._msSinceLastCalculation+=s,this._msSinceLastCalculation>=this._calculateFpsEachMs&&(this._msSinceLastCalculation=0,this.calculateAverageFps(),this.checkAndNotifyLowFps())}onLowFps(s,t){this._minFps=s,this._lowFpsCallback=t}collectFps(s){this._lastChecks.push(Math.round(1e3/s))}calculateAverageFps(){const s=this._lastChecks.reduce(((s,t)=>s+t),0);this._currentFps=Math.round(s/this._lastChecks.length),this._lastChecks=[]}checkAndNotifyLowFps(){this._minFps&&this._currentFps<this._minFps&&(this._lowFpsInRow++,5===this._lowFpsInRow&&(this._lowFpsInRow=0,this._lowFpsCallback&&this._lowFpsCallback()))}}class i{_maxMsBetweenTicks;_expectedMsBetweenTicks;_ticksMissed=1;_msBetweenTicks=0;_lastTickTime=0;_requestFrameId;_tickCallback;constructor(s,t){this._maxMsBetweenTicks=1e3/s,this._expectedMsBetweenTicks=1e3/t}start(){this._tickCallback&&(this._lastTickTime=performance.now(),this.loop())}stop(){this._requestFrameId&&(window.cancelAnimationFrame(this._requestFrameId),this._requestFrameId=void 0)}isStarted(){return!!this._requestFrameId}onTick(s){this._tickCallback=s}msBetweenTicks(){return this._msBetweenTicks}ticksMissed(){return this._ticksMissed}loop(){const s=performance.now();this._msBetweenTicks=s-this._lastTickTime,this._ticksMissed=this._msBetweenTicks/this._expectedMsBetweenTicks,this._msBetweenTicks>=this._maxMsBetweenTicks&&(this._lastTickTime=s,this._tickCallback&&this._tickCallback(this._msBetweenTicks)),this._requestFrameId=window.requestAnimationFrame(this.loop.bind(this))}}class c{_expectedMsBetweenTicks;_ticksMissed=1;_msBetweenTicks=0;_lastTickTime=0;_timeoutId;_tickCallback;constructor(s){this._expectedMsBetweenTicks=1e3/s}start(){this._tickCallback&&(this._lastTickTime=performance.now(),this.delayLoop())}stop(){this._timeoutId&&(clearTimeout(this._timeoutId),this._timeoutId=void 0)}isStarted(){return!!this._timeoutId}onTick(s){this._tickCallback=s}msBetweenTicks(){return this._msBetweenTicks}ticksMissed(){return this._ticksMissed}delayLoop(){this._timeoutId=setTimeout((()=>this.loop()),this._expectedMsBetweenTicks)}loop(){const s=performance.now();this._msBetweenTicks=s-this._lastTickTime,this._ticksMissed=this._msBetweenTicks/this._expectedMsBetweenTicks,this._lastTickTime=s,this._tickCallback&&this._tickCallback(this._msBetweenTicks),this.delayLoop()}}class a{_fps=new e;_logicTicker;_drawTicker;constructor(s){const t=s?.minFps||0,e=this.compensatedMaxFps(s?.maxFps||60),a=s?.expectedFps||60,r=s?.onLogicTick,o=s?.onDrawTick,h=s?.onLowFps;this._logicTicker=new c(a),this._drawTicker=new i(e,a),r&&this._logicTicker.onTick(r),o&&this._drawTicker.onTick((s=>{o(),this._fps.calculate(s)})),h&&this._fps.onLowFps(t,h)}start(){this._logicTicker.start(),this._drawTicker.start()}stop(){this._logicTicker.stop(),this._drawTicker.stop()}isStarted(){return this._logicTicker.isStarted()||this._drawTicker.isStarted()}fps(){return this._fps.currentFps()}msBetweenTicks(){return this._logicTicker.isStarted()?this._logicTicker.msBetweenTicks():this._drawTicker.msBetweenTicks()}ticksMissed(){return this._logicTicker.isStarted()?this._logicTicker.ticksMissed():this._drawTicker.ticksMissed()}compensatedMaxFps(s){return Math.floor(1.1*s)+10*Math.floor(s/100)}}var r=t.R;export{r as Ticker}; |
| !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("Ticker",[],t):"object"==typeof exports?exports.Ticker=t():e.Ticker=t()}(self,(()=>(()=>{"use strict";var e={d:(t,s)=>{for(var i in s)e.o(s,i)&&!e.o(t,i)&&Object.defineProperty(t,i,{enumerable:!0,get:s[i]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{Ticker:()=>r});class s{_minFps;_currentFps=60;_lastChecks=[];_calculateFpsEachMs=2e3;_msSinceLastCalculation=0;_lowFpsInRow=0;_lowFpsCallback;currentFps(){return this._currentFps}calculate(e){this.collectFps(e),this._msSinceLastCalculation+=e,this._msSinceLastCalculation>=this._calculateFpsEachMs&&(this._msSinceLastCalculation=0,this.calculateAverageFps(),this.checkAndNotifyLowFps())}onLowFps(e,t){this._minFps=e,this._lowFpsCallback=t}collectFps(e){this._lastChecks.push(Math.round(1e3/e))}calculateAverageFps(){const e=this._lastChecks.reduce(((e,t)=>e+t),0);this._currentFps=Math.round(e/this._lastChecks.length),this._lastChecks=[]}checkAndNotifyLowFps(){this._minFps&&this._currentFps<this._minFps&&(this._lowFpsInRow++,5===this._lowFpsInRow&&(this._lowFpsInRow=0,this._lowFpsCallback&&this._lowFpsCallback()))}}class i{_maxMsBetweenTicks;_expectedMsBetweenTicks;_ticksMissed=1;_msBetweenTicks=0;_lastTickTime=0;_requestFrameId;_tickCallback;constructor(e,t){this._maxMsBetweenTicks=1e3/e,this._expectedMsBetweenTicks=1e3/t}start(){this._tickCallback&&(this._lastTickTime=performance.now(),this.loop())}stop(){this._requestFrameId&&(window.cancelAnimationFrame(this._requestFrameId),this._requestFrameId=void 0)}isStarted(){return!!this._requestFrameId}onTick(e){this._tickCallback=e}msBetweenTicks(){return this._msBetweenTicks}ticksMissed(){return this._ticksMissed}loop(){const e=performance.now();this._msBetweenTicks=e-this._lastTickTime,this._ticksMissed=this._msBetweenTicks/this._expectedMsBetweenTicks,this._msBetweenTicks>=this._maxMsBetweenTicks&&(this._lastTickTime=e,this._tickCallback&&this._tickCallback(this._msBetweenTicks)),this._requestFrameId=window.requestAnimationFrame(this.loop.bind(this))}}class c{_expectedMsBetweenTicks;_ticksMissed=1;_msBetweenTicks=0;_lastTickTime=0;_timeoutId;_tickCallback;constructor(e){this._expectedMsBetweenTicks=1e3/e}start(){this._tickCallback&&(this._lastTickTime=performance.now(),this.delayLoop())}stop(){this._timeoutId&&(clearTimeout(this._timeoutId),this._timeoutId=void 0)}isStarted(){return!!this._timeoutId}onTick(e){this._tickCallback=e}msBetweenTicks(){return this._msBetweenTicks}ticksMissed(){return this._ticksMissed}delayLoop(){this._timeoutId=setTimeout((()=>this.loop()),this._expectedMsBetweenTicks)}loop(){const e=performance.now();this._msBetweenTicks=e-this._lastTickTime,this._ticksMissed=this._msBetweenTicks/this._expectedMsBetweenTicks,this._lastTickTime=e,this._tickCallback&&this._tickCallback(this._msBetweenTicks),this.delayLoop()}}class r{_fps=new s;_logicTicker;_drawTicker;constructor(e){const t=e?.minFps||0,s=this.compensatedMaxFps(e?.maxFps||60),r=e?.expectedFps||60,o=e?.onLogicTick,a=e?.onDrawTick,n=e?.onLowFps;this._logicTicker=new c(r),this._drawTicker=new i(s,r),o&&this._logicTicker.onTick(o),a&&this._drawTicker.onTick((e=>{a(),this._fps.calculate(e)})),n&&this._fps.onLowFps(t,n)}start(){this._logicTicker.start(),this._drawTicker.start()}stop(){this._logicTicker.stop(),this._drawTicker.stop()}isStarted(){return this._logicTicker.isStarted()||this._drawTicker.isStarted()}fps(){return this._fps.currentFps()}msBetweenTicks(){return this._logicTicker.isStarted()?this._logicTicker.msBetweenTicks():this._drawTicker.msBetweenTicks()}ticksMissed(){return this._logicTicker.isStarted()?this._logicTicker.ticksMissed():this._drawTicker.ticksMissed()}compensatedMaxFps(e){return Math.floor(1.1*e)+10*Math.floor(e/100)}}return t})())); |
+13
-8
| { | ||
| "name": "@armniko/ticker", | ||
| "version": "1.0.0", | ||
| "version": "1.1.0", | ||
| "description": "Javascript/typescript library for running app loop with separate logical/drawing ticks and FPS limitation.", | ||
@@ -15,4 +15,6 @@ "author": "Armīns Nikolajevs <armins.nikolajevs@gmail.com>", | ||
| ], | ||
| "main": "dist/index.js", | ||
| "types": "dist/index.d.ts", | ||
| "main": "./dist/index.umd.js", | ||
| "module": "./dist/index.esm.js", | ||
| "typings": "./dist/index.d.ts", | ||
| "type": "module", | ||
| "files": [ | ||
@@ -22,7 +24,7 @@ "dist" | ||
| "scripts": { | ||
| "build": "tsc --project tsconfig.build.json", | ||
| "dev": "node test/server/main.js & tsc --watch", | ||
| "lint": "eslint src --max-warnings=0", | ||
| "build": "rimraf dist && webpack", | ||
| "dev": "node test/server/main.js & webpack --watch", | ||
| "lint": "tsc --noEmit && eslint src --max-warnings=0", | ||
| "lint:fix": "npm run lint -- --fix", | ||
| "test": "jest --runInBand", | ||
| "test": "npm run build && jest", | ||
| "test:coverage": "npm run test -- --collect-coverage" | ||
@@ -42,4 +44,7 @@ }, | ||
| "ts-jest": "^29.1.2", | ||
| "typescript": "^5.4.3" | ||
| "ts-loader": "^9.5.1", | ||
| "typescript": "^5.4.3", | ||
| "webpack": "^5.91.0", | ||
| "webpack-cli": "^5.1.4" | ||
| } | ||
| } |
+17
-0
@@ -78,1 +78,18 @@ <h1 align="center">Ticker</h1> | ||
| ``` | ||
| ## Changelog | ||
| <table> | ||
| <tr> | ||
| <td>v1.1.0</td> | ||
| <td> | ||
| Precompile UMD and ESM<br> | ||
| </td> | ||
| </tr> | ||
| <tr> | ||
| <td>v1.0.0</td> | ||
| <td> | ||
| Initial version | ||
| </td> | ||
| </tr> | ||
| </table> |
| /* eslint-disable @typescript-eslint/no-unused-vars */ | ||
| export * from './libraries/ticker'; |
| export {}; |
| export class Fps { | ||
| _minFps; | ||
| _currentFps = 60; | ||
| _lastChecks = []; | ||
| _calculateFpsEachMs = 2000; | ||
| _msSinceLastCalculation = 0; | ||
| _lowFpsInRow = 0; | ||
| _lowFpsCallback; | ||
| currentFps() { | ||
| return this._currentFps; | ||
| } | ||
| calculate(msSinceLastTick) { | ||
| this.collectFps(msSinceLastTick); | ||
| this._msSinceLastCalculation += msSinceLastTick; | ||
| if (this._msSinceLastCalculation >= this._calculateFpsEachMs) { | ||
| this._msSinceLastCalculation = 0; | ||
| this.calculateAverageFps(); | ||
| this.checkAndNotifyLowFps(); | ||
| } | ||
| } | ||
| onLowFps(minFps, callback) { | ||
| this._minFps = minFps; | ||
| this._lowFpsCallback = callback; | ||
| } | ||
| collectFps(msSinceLastTick) { | ||
| this._lastChecks.push(Math.round(1000 / msSinceLastTick)); | ||
| } | ||
| calculateAverageFps() { | ||
| const sumFps = this._lastChecks.reduce((a, b) => a + b, 0); | ||
| this._currentFps = Math.round(sumFps / this._lastChecks.length); | ||
| this._lastChecks = []; | ||
| } | ||
| checkAndNotifyLowFps() { | ||
| if (this._minFps && this._currentFps < this._minFps) { | ||
| this._lowFpsInRow++; | ||
| if (this._lowFpsInRow === 5) { | ||
| this._lowFpsInRow = 0; | ||
| if (this._lowFpsCallback) { | ||
| this._lowFpsCallback(); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
| import { Fps } from './fps/fps'; | ||
| import { DrawTicker } from './tickers/draw-ticker'; | ||
| import { LogicTicker } from './tickers/logic-ticker'; | ||
| export class Ticker { | ||
| _fps = new Fps(); | ||
| _logicTicker; | ||
| _drawTicker; | ||
| constructor(options) { | ||
| const minFps = options?.minFps || 0; | ||
| const maxFps = this.compensatedMaxFps(options?.maxFps || 60); | ||
| const expectedFps = options?.expectedFps || 60; | ||
| const onLogicTick = options?.onLogicTick; | ||
| const onDrawTick = options?.onDrawTick; | ||
| const onLowFps = options?.onLowFps; | ||
| this._logicTicker = new LogicTicker(expectedFps); | ||
| this._drawTicker = new DrawTicker(maxFps, expectedFps); | ||
| if (onLogicTick) { | ||
| this._logicTicker.onTick(onLogicTick); | ||
| } | ||
| if (onDrawTick) { | ||
| this._drawTicker.onTick((msSinceLastTick) => { | ||
| onDrawTick(); | ||
| this._fps.calculate(msSinceLastTick); | ||
| }); | ||
| } | ||
| if (onLowFps) { | ||
| this._fps.onLowFps(minFps, onLowFps); | ||
| } | ||
| } | ||
| start() { | ||
| this._logicTicker.start(); | ||
| this._drawTicker.start(); | ||
| } | ||
| stop() { | ||
| this._logicTicker.stop(); | ||
| this._drawTicker.stop(); | ||
| } | ||
| isStarted() { | ||
| return this._logicTicker.isStarted() || this._drawTicker.isStarted(); | ||
| } | ||
| fps() { | ||
| return this._fps.currentFps(); | ||
| } | ||
| msBetweenTicks() { | ||
| return this._logicTicker.isStarted() ? this._logicTicker.msBetweenTicks() : this._drawTicker.msBetweenTicks(); | ||
| } | ||
| ticksMissed() { | ||
| return this._logicTicker.isStarted() ? this._logicTicker.ticksMissed() : this._drawTicker.ticksMissed(); | ||
| } | ||
| compensatedMaxFps(maxFps) { | ||
| return Math.floor(maxFps * 1.1) + Math.floor(maxFps / 100) * 10; | ||
| } | ||
| } |
| export class DrawTicker { | ||
| _maxMsBetweenTicks; | ||
| _expectedMsBetweenTicks; | ||
| _ticksMissed = 1; | ||
| _msBetweenTicks = 0; | ||
| _lastTickTime = 0; | ||
| _requestFrameId; | ||
| _tickCallback; | ||
| constructor(maxFps, expectedFps) { | ||
| this._maxMsBetweenTicks = 1000 / maxFps; | ||
| this._expectedMsBetweenTicks = 1000 / expectedFps; | ||
| } | ||
| start() { | ||
| if (this._tickCallback) { | ||
| this._lastTickTime = performance.now(); | ||
| this.loop(); | ||
| } | ||
| } | ||
| stop() { | ||
| if (this._requestFrameId) { | ||
| window.cancelAnimationFrame(this._requestFrameId); | ||
| this._requestFrameId = undefined; | ||
| } | ||
| } | ||
| isStarted() { | ||
| return !!this._requestFrameId; | ||
| } | ||
| onTick(callback) { | ||
| this._tickCallback = callback; | ||
| } | ||
| msBetweenTicks() { | ||
| return this._msBetweenTicks; | ||
| } | ||
| ticksMissed() { | ||
| return this._ticksMissed; | ||
| } | ||
| loop() { | ||
| const now = performance.now(); | ||
| this._msBetweenTicks = now - this._lastTickTime; | ||
| this._ticksMissed = this._msBetweenTicks / this._expectedMsBetweenTicks; | ||
| if (this._msBetweenTicks >= this._maxMsBetweenTicks) { | ||
| this._lastTickTime = now; | ||
| if (this._tickCallback) { | ||
| this._tickCallback(this._msBetweenTicks); | ||
| } | ||
| } | ||
| this._requestFrameId = window.requestAnimationFrame(this.loop.bind(this)); | ||
| } | ||
| } |
| export class LogicTicker { | ||
| _expectedMsBetweenTicks; | ||
| _ticksMissed = 1; | ||
| _msBetweenTicks = 0; | ||
| _lastTickTime = 0; | ||
| _timeoutId; | ||
| _tickCallback; | ||
| constructor(expectedFps) { | ||
| this._expectedMsBetweenTicks = 1000 / expectedFps; | ||
| } | ||
| start() { | ||
| if (this._tickCallback) { | ||
| this._lastTickTime = performance.now(); | ||
| this.delayLoop(); | ||
| } | ||
| } | ||
| stop() { | ||
| if (this._timeoutId) { | ||
| clearTimeout(this._timeoutId); | ||
| this._timeoutId = undefined; | ||
| } | ||
| } | ||
| isStarted() { | ||
| return !!this._timeoutId; | ||
| } | ||
| onTick(callback) { | ||
| this._tickCallback = callback; | ||
| } | ||
| msBetweenTicks() { | ||
| return this._msBetweenTicks; | ||
| } | ||
| ticksMissed() { | ||
| return this._ticksMissed; | ||
| } | ||
| delayLoop() { | ||
| this._timeoutId = setTimeout(() => this.loop(), this._expectedMsBetweenTicks); | ||
| } | ||
| loop() { | ||
| const now = performance.now(); | ||
| this._msBetweenTicks = now - this._lastTickTime; | ||
| this._ticksMissed = this._msBetweenTicks / this._expectedMsBetweenTicks; | ||
| this._lastTickTime = now; | ||
| if (this._tickCallback) { | ||
| this._tickCallback(this._msBetweenTicks); | ||
| } | ||
| this.delayLoop(); | ||
| } | ||
| } |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
14765
16.1%95
21.79%Yes
NaN15
25%11
-26.67%72
-73.23%3
200%1
Infinity%