Skip to content

Commit f358dd2

Browse files
committed
[IMPL] useFPS hook
1 parent ab1dc6d commit f358dd2

11 files changed

Lines changed: 146 additions & 11 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { useFPS } from "../../../../../../packages/react-tools/src"
2+
3+
/**
4+
The component uses _useFPS_ hook to compute FPS and shows details on screen.
5+
*/
6+
export const UseFPS = () => {
7+
const fps = useFPS();
8+
return <p>FPS: {fps.currentFps} - avg: {fps.avg} - Max FPS: {fps.maxFps}</p>
9+
}

apps/react-tools-demo/src/constants/components.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export const COMPONENTS = [
4242
"useCallbackDeepCompare",
4343
"useLazyRef",
4444
"useMergedRef",
45-
"useId"
45+
"useId",
4646
],
4747
//EVENTS
4848
[
@@ -99,7 +99,8 @@ export const COMPONENTS = [
9999
"useBluetooth",
100100
"useScreenWakeLock",
101101
"useSpeechRecognition",
102-
"useSpeechSynthesis"
102+
"useSpeechSynthesis",
103+
"useFPS"
103104
]
104105
],
105106
//UTILS
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# useFPS
2+
Hook to detect FPS (Frames per second).
3+
4+
## Usage
5+
6+
```tsx
7+
export const UseFPS = () => {
8+
const fps = useFPS();
9+
return <p>FPS: {fps.currentFps} - avg: {fps.avg} - Max FPS: {fps.maxFps}</p>
10+
}
11+
```
12+
13+
> The component uses _useFPS_ hook to compute FPS and shows details on screen.
14+
15+
16+
## API
17+
18+
```tsx
19+
useFPS({ everySeconds, windowSize }:UseFPSProps={windowSize:10, everySeconds:.5}): UseFPSResult
20+
```
21+
22+
> ### Params
23+
>
24+
> - __opts?__: _UseFPSProps_
25+
configuration options to detect FPS.
26+
> - __opts.everySeconds=0.5?__: _number_
27+
it indicates how often to compute FPS. Default is 0.5 second.
28+
> - __opts.windowSize=10?__: _number_
29+
it indicates how FPS result keep in memory and computing average. Default is 10.
30+
>
31+
32+
> ### Returns
33+
>
34+
> __result__: _UseFPSResult_
35+
> Stateful object with these properties:
36+
> - __fps__: array of computed FPS by _windowSize_.
37+
> - __currentFps__: current FPS value.
38+
> - __avg__: average of FPS values kept in memory.
39+
> - __maxFps__: maximum FPS value computed.
40+
>

apps/react-tools-demo/src/markdown/useRaf.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ useRaf<T extends unknown[]>(cb: (timer: number, repeat: ()=>void, ...args: T) =>
3333
3434
> ### Params
3535
>
36-
> - __cb__: _(timer:number, ()=>void, ...args: T) => void_
36+
> - __cb__: _(timer:number, repeat:()=>void, ...args: T)=>void_
3737
callback to execute prior to the next repaint. In addition to the classic timeStamp parameter, which indicates the end time of rendering of the previous frame, the second parameter is a function which, if invoked, re-executes the requestAnimationFrame with the callback itself, and finally various parameters can be added, passed with the invocation function returned by the hook.
3838
>
3939

packages/react-tools/README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878
- [x] useDebounce
7979
- [x] useThrottle
8080
- [x] useActiveElement
81-
- [x] useRaf (with a reference to call itself again)
81+
- [x] useRaf
8282
- [x] useTimeout
8383
- [x] useInterval
8484
- [x] useTextSelection
@@ -88,7 +88,7 @@
8888
- [x] useReducedMotion
8989
- [x] useTitle
9090
- [x] useIdle
91-
- [x] useFullscreen (check browser compatibility)
91+
- [x] useFullscreen
9292
- [x] useBattery
9393
- [x] useGeolocation
9494
- [x] useShare
@@ -101,8 +101,7 @@
101101
- [x] useScreenWakeLock
102102
- [x] useSpeechRecognition
103103
- [x] useSpeechSynthesis
104-
- [ ] useDevicesList (https://vueuse.org/core/useDevicesList/)
105-
- [ ] useFPS (https://vueuse.org/core/useFps/)
104+
- [x] useFPS
106105
- [ ] useParallax (https://vueuse.org/core/useParallax/)
107106
- [ ] useScrollLock (https://vueuse.org/core/useScrollLock/)
108107
- [ ] useAnimate (https://vueuse.org/core/useAnimate/)
@@ -113,6 +112,8 @@
113112
- [ ] useFetch (with suspense ???)
114113
- [ ] useAsync
115114
- [ ] useMedia
115+
- [ ] useDevicesList (https://vueuse.org/core/useDevicesList/)
116+
- [ ] useDisplayMedia
116117
- [ ] useScreenShare
117118
- [ ] useMediaDevices
118119
- [ ] useMediaStream

packages/react-tools/src/hooks/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,5 @@ export { useDerivedState } from './useDerivedState';
7979
export { useBluetooth } from './useBluetooth';
8080
export { useScreenWakeLock } from './useScreenWakeLock';
8181
export { useSpeechRecognition } from './useSpeechRecognition';
82-
export { useSpeechSynthesis } from './useSpeechSynthesis';
82+
export { useSpeechSynthesis } from './useSpeechSynthesis';
83+
export { useFPS } from './useFPS';
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { useRef, useState } from "react"
2+
import { useEffectOnce, useRaf } from ".";
3+
import { UseFPSProps, UseFPSResult } from "../models";
4+
5+
/**
6+
* **`useFPS`**: Hook to detect FPS (Frames per second).
7+
* @param {UseFPSProps} [opts] - configuration options to detect FPS.
8+
* @param {number} [opts.everySeconds=0.5] - it indicates how often to compute FPS. Default is 0.5 second.
9+
* @param {number} [opts.windowSize=10] - it indicates how FPS result keep in memory and computing average. Default is 10.
10+
* @returns {UseFPSResult} result
11+
* Stateful object with these properties:
12+
* - __fps__: array of computed FPS by _windowSize_.
13+
* - __currentFps__: current FPS value.
14+
* - __avg__: average of FPS values kept in memory.
15+
* - __maxFps__: maximum FPS value computed.
16+
*/
17+
export const useFPS = ({ everySeconds, windowSize }:UseFPSProps={windowSize:10, everySeconds:.5}): UseFPSResult => {
18+
const [fps, setFps] = useState<UseFPSResult>({fps: [], avg: 0, maxFps: 0, currentFps: 0});
19+
const state = useRef<{ started: boolean, ratings: number[], frames: number, startTime: number, lastTime: number, fps: number[], avg: number, maxFps: number, currentFps: number, calc: (timer: number, repeat: () => void) => void }>({
20+
started: false,
21+
ratings: [],
22+
frames: 0,
23+
startTime: performance.now(),
24+
lastTime: 0,
25+
fps: [],
26+
avg:0,
27+
maxFps: 0,
28+
currentFps: 0,
29+
calc: (timer: number, repeat: () => void) => {
30+
state.current.frames++;
31+
state.current.lastTime = timer;
32+
if (state.current.lastTime > state.current.startTime+(everySeconds*1000)) {
33+
const elapsedTime = state.current.lastTime - state.current.startTime;
34+
let currentFps = Math.round(1000 / (elapsedTime/state.current.frames));
35+
let fps = [...state.current.fps, currentFps];
36+
fps = fps.slice(Math.max(state.current.fps.length - windowSize, 0));
37+
const avg = +(fps.reduce((a, b) => a + b, 0) / fps.length).toFixed(2);
38+
const maxFps = Math.max(currentFps, state.current.maxFps);
39+
currentFps = fps.at(-1) as number;
40+
if (fps.some((el, index) => state.current.fps[index] !== el) || avg !== state.current.avg || maxFps !== state.current.maxFps || currentFps !== state.current.currentFps) {
41+
state.current.fps = fps;
42+
state.current.currentFps = currentFps;
43+
state.current.avg = avg;
44+
state.current.maxFps = maxFps;
45+
setFps({
46+
fps,
47+
currentFps,
48+
maxFps,
49+
avg
50+
})
51+
}
52+
53+
state.current.startTime = timer;
54+
state.current.frames = 0;
55+
}
56+
repeat();
57+
}
58+
});
59+
const [start, cancel] = useRaf(state.current.calc)
60+
if (!state.current.started) {
61+
state.current.started = true;
62+
start();
63+
}
64+
65+
useEffectOnce(() => () => cancel());
66+
67+
return fps;
68+
}

packages/react-tools/src/hooks/useRaf.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { useCallback, useRef } from "react"
22

33
/**
44
* **`useRaf`**: Hook to execute a callback function with _requestAnimationFrame_ to optimize performance. Refer to (requestAnimationFrame)[https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame].
5-
* @param {(timer:number, ()=>void, ...args: T) => void} cb - callback to execute prior to the next repaint. In addition to the classic timeStamp parameter, which indicates the end time of rendering of the previous frame, the second parameter is a function which, if invoked, re-executes the requestAnimationFrame with the callback itself, and finally various parameters can be added, passed with the invocation function returned by the hook.
5+
* @param {(timer:number, repeat:()=>void, ...args: T)=>void} cb - callback to execute prior to the next repaint. In addition to the classic timeStamp parameter, which indicates the end time of rendering of the previous frame, the second parameter is a function which, if invoked, re-executes the requestAnimationFrame with the callback itself, and finally various parameters can be added, passed with the invocation function returned by the hook.
66
* @returns {[(...args: T)=>void, ()=>void]} results - array with __start__ function to invoke _requestAnimationFrame_ and __cancel__ function to invoke _cancelAnimationFrame_.
77
*/
88
export const useRaf = <T extends unknown[]>(cb: (timer: number, repeat: ()=>void, ...args: T) => void): [(...args: T)=>void, ()=>void] => {

packages/react-tools/src/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ export type {
4141
UseSpeechSynthesis,
4242
UseSpeechSynthesisProps,
4343
SpeechSynthesisSpeakParam,
44+
UseFPSProps,
45+
UseFPSResult
4446
} from './models'
4547

4648
export {
@@ -125,7 +127,8 @@ export {
125127
useBluetooth,
126128
useScreenWakeLock,
127129
useSpeechRecognition,
128-
useSpeechSynthesis
130+
useSpeechSynthesis,
131+
useFPS
129132
} from './hooks'
130133

131134
export {

packages/react-tools/src/models/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ export type { DeviceMotionProps } from './useDeviceMotion.model';
1010
export type { DeviceOrientationProps } from './useDeviceOrientation.model';
1111
export type { Bluetooth, BluetoothCharacteristicProperties, BluetoothCharacteristicUUID, BluetoothDescriptorUUID, BluetoothDevice, BluetoothDevicesOptions, BluetoothRemoteGATTCharacteristic, BluetoothRemoteGATTDescriptor, BluetoothRemoteGATTServer, BluetoothRemoteGATTService, BluetoothScanFilters, BluetoothServiceUUID } from './useBluetooth.model';
1212
export type { UseSpeechRecognitionProps, SpeechRecognition, SpeechRecognitionConfig, SpeechRecognitionState, SpeechGrammar, SpeechGrammarList, SpeechRecognitionErrorCode, SpeechRecognitionErrorEvent, SpeechRecognitionEvent } from './useSpeechRecognition.model';
13-
export type { SpeechSynthesisSpeakParam, UseSpeechSynthesis, UseSpeechSynthesisProps } from './useSpeechSynthesis.model';
13+
export type { SpeechSynthesisSpeakParam, UseSpeechSynthesis, UseSpeechSynthesisProps } from './useSpeechSynthesis.model';
14+
export type { UseFPSProps, UseFPSResult} from './useFPS.model';

0 commit comments

Comments
 (0)