UNPKG

4.59 kBJavaScriptView Raw
1const MILLISECONDS_IN_SECOND = 1000;
2
3const FILTER_STRENGTH = Symbol();
4const SAMPLE_RATE = Symbol();
5const HISTORY_DURATION = Symbol();
6const FPS = Symbol();
7const HISTORY = Symbol();
8const SAMPLE_INTERVAL = Symbol();
9const ON_SAMPLE = Symbol();
10
11/**
12 * @name Installation
13 * @summary
14 *
15 * ```
16 * npm install framerate-history
17 * ```
18 */
19
20/**
21 * @category 2
22 * @class FrameRate
23 * @summary
24 *
25 * ``` javascript
26 * import FrameRate from 'framerate-history';
27 * ```
28 *
29 * @arg {Object} settings
30 * @arg {Function} [settings.onSample]
31 * @arg {Number} [settings.filterStrength=5]
32 * @arg {Number} [settings.historyDuration=30]
33 * @arg {Number} [settings.sampleRate=10]
34 */
35export default class FrameRate {
36 constructor(settings) {
37 const self = this;
38 let frameTime = 0;
39 let lastLoop = performance.now();
40
41 self[HISTORY] = [];
42
43 self.onSample(settings.onSample);
44 self.filterStrength(settings.filterStrength || 5);
45 self.historyDuration(settings.historyDuration || 30);
46 self.sampleRate(settings.sampleRate || 10);
47
48 const refreshLoop = () => {
49 window.requestAnimationFrame((now) => {
50 frameTime += (now - lastLoop - frameTime) / self[FILTER_STRENGTH];
51 lastLoop = now;
52 self[FPS] = Math.round(MILLISECONDS_IN_SECOND / frameTime);
53 refreshLoop();
54 });
55 };
56
57 refreshLoop();
58 }
59
60 /**
61 * The last recorded FPS
62 *
63 * @memberOf FrameRate
64 * @instance
65 * @readonly
66 *
67 * @returns {Number}
68 */
69 get fps() {
70 return this[FPS];
71 }
72
73 /**
74 * The FPS recordings over the history duration
75 *
76 * @memberOf FrameRate
77 * @instance
78 * @readonly
79 *
80 * @returns {Array}
81 */
82 get history() {
83 return this[HISTORY];
84 }
85
86 /**
87 * Sets a filter on the frame rate calculation. Setting to 1 will effectively turn off the filter, the higher the
88 * number the more smooth the curve over time. See this stackoverflow question for details:
89 * https://stackoverflow.com/questions/4787431/check-fps-in-js
90 *
91 * @default 5
92 * @memberOf FrameRate
93 * @chainable
94 * @instance
95 *
96 * @arg {Number} [value]
97 *
98 * @returns {Number}
99 */
100 filterStrength(value) {
101 const self = this;
102
103 if (value !== undefined) {
104 self[FILTER_STRENGTH] = value;
105
106 return self;
107 }
108
109 return self[FILTER_STRENGTH];
110 }
111
112 /**
113 * The rate to take samples. Setting to 0 will clear the interval. If the interval is prevented from executing at the desired rate, the history will get filled in with the current frame rate in an attempt to keep the history as accurate as possible.
114 *
115 * @default 10
116 * @memberOf FrameRate
117 * @chainable
118 * @instance
119 *
120 * @arg {Number} [value] - Samples per second
121 *
122 * @returns {Number}
123 */
124 sampleRate(value) {
125 const self = this;
126 let lastSample;
127 let now;
128
129 if (value !== undefined) {
130 const DURATION = MILLISECONDS_IN_SECOND / value;
131 self[SAMPLE_RATE] = value;
132 self.historyDuration(self.historyDuration());
133 lastSample = performance.now();
134
135 clearInterval(self[SAMPLE_INTERVAL]);
136
137 if (self[SAMPLE_RATE] && self.historyDuration()) {
138 self[SAMPLE_INTERVAL] = setInterval(() => {
139 now = performance.now();
140
141 while (lastSample + DURATION < now) {
142 self[HISTORY].push(self[FPS]);
143 self[HISTORY].shift();
144 lastSample += DURATION;
145 }
146
147 if (self[ON_SAMPLE]) {
148 self[ON_SAMPLE](self[HISTORY]);
149 }
150 }, DURATION);
151 }
152
153 return self;
154 }
155
156 return self[SAMPLE_RATE];
157 }
158
159 /**
160 * The callback will get called for every sample taken.
161 *
162 * @memberOf FrameRate
163 * @chainable
164 * @instance
165 *
166 * @arg {Function} [callback] - The callback is given one param, the FPS history array.
167 *
168 * @returns {Function}
169 */
170 onSample(callback) {
171 const self = this;
172
173 if (callback !== undefined) {
174 self[ON_SAMPLE] = callback;
175
176 return self;
177 }
178
179 return self[ON_SAMPLE];
180 }
181
182 /**
183 * Defines the duration of tracked history.
184 *
185 * @default 30
186 * @memberOf FrameRate
187 * @chainable
188 * @instance
189 *
190 * @arg {Number} [value] - Seconds
191 *
192 * @returns {Number}
193 */
194 historyDuration(value) {
195 const self = this;
196
197 if (value !== undefined) {
198 self[HISTORY_DURATION] = value;
199 const totalHistory = self[SAMPLE_RATE] * self[HISTORY_DURATION];
200
201 while (self[HISTORY].length < totalHistory) {
202 self[HISTORY].unshift(0);
203 }
204 while (self[HISTORY].length > totalHistory) {
205 self[HISTORY].shift();
206 }
207
208 return self;
209 }
210
211 return self[HISTORY_DURATION];
212 }
213}