@vyshnav18/react-native-fps-counter 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/README.md +101 -0
  2. package/android/.gradle/9.2.0/checksums/checksums.lock +0 -0
  3. package/android/.gradle/9.2.0/checksums/md5-checksums.bin +0 -0
  4. package/android/.gradle/9.2.0/checksums/sha1-checksums.bin +0 -0
  5. package/android/.gradle/9.2.0/fileChanges/last-build.bin +0 -0
  6. package/android/.gradle/9.2.0/fileHashes/fileHashes.bin +0 -0
  7. package/android/.gradle/9.2.0/fileHashes/fileHashes.lock +0 -0
  8. package/android/.gradle/9.2.0/gc.properties +0 -0
  9. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  10. package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
  11. package/android/.gradle/vcs-1/gc.properties +0 -0
  12. package/android/build/reports/problems/problems-report.html +659 -0
  13. package/android/build.gradle +64 -0
  14. package/android/src/main/AndroidManifest.xml +3 -0
  15. package/android/src/main/java/com/rnfpscounter/FpsCounterModule.kt +120 -0
  16. package/android/src/main/java/com/rnfpscounter/FpsCounterPackage.kt +16 -0
  17. package/dist/index.d.mts +110 -0
  18. package/dist/index.d.ts +110 -0
  19. package/dist/index.js +192 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/index.mjs +188 -0
  22. package/dist/index.mjs.map +1 -0
  23. package/ios/FpsCounterModule.h +12 -0
  24. package/ios/FpsCounterModule.mm +123 -0
  25. package/ios/RNFpsCounter.podspec +21 -0
  26. package/package.json +73 -0
  27. package/react-native.config.js +14 -0
  28. package/src/FpsCounter.tsx +190 -0
  29. package/src/index.ts +10 -0
  30. package/src/specs/NativeFpsCounter.ts +26 -0
  31. package/src/useFPSMetric.ts +82 -0
  32. package/src/useUIFPSMetric.ts +81 -0
@@ -0,0 +1,26 @@
1
+ import type { TurboModule } from 'react-native';
2
+ import { TurboModuleRegistry } from 'react-native';
3
+
4
+ export interface Spec extends TurboModule {
5
+ /**
6
+ * Start monitoring UI thread FPS
7
+ */
8
+ startMonitoring(): void;
9
+
10
+ /**
11
+ * Stop monitoring UI thread FPS
12
+ */
13
+ stopMonitoring(): void;
14
+
15
+ /**
16
+ * Required for EventEmitter support
17
+ */
18
+ addListener(eventName: string): void;
19
+
20
+ /**
21
+ * Required for EventEmitter support
22
+ */
23
+ removeListeners(count: number): void;
24
+ }
25
+
26
+ export default TurboModuleRegistry.getEnforcing<Spec>('FpsCounter');
@@ -0,0 +1,82 @@
1
+ import { useEffect, useState } from 'react';
2
+
3
+ type FrameData = {
4
+ fps: number;
5
+ lastStamp: number;
6
+ framesCount: number;
7
+ average: number;
8
+ totalCount: number;
9
+ };
10
+
11
+ export type FPS = {
12
+ average: FrameData['average'];
13
+ fps: FrameData['fps'];
14
+ };
15
+
16
+ /**
17
+ * A custom hook that tracks frames per second (FPS) in React Native.
18
+ * Uses requestAnimationFrame to count frames and updates at most once per second.
19
+ *
20
+ * @returns An object containing the current FPS and rolling average FPS
21
+ *
22
+ * @example
23
+ * ```tsx
24
+ * const { fps, average } = useFPSMetric();
25
+ * console.log(`Current: ${fps} FPS, Average: ${average.toFixed(2)} FPS`);
26
+ * ```
27
+ */
28
+ export function useFPSMetric(): FPS {
29
+ const [frameState, setFrameState] = useState<FrameData>({
30
+ fps: 0,
31
+ lastStamp: Date.now(),
32
+ framesCount: 0,
33
+ average: 0,
34
+ totalCount: 0,
35
+ });
36
+
37
+ useEffect(() => {
38
+ // NOTE: timeout is here because requestAnimationFrame is deferred
39
+ // and to prevent setStates when unmounted
40
+ let timeout: ReturnType<typeof setTimeout> | null = null;
41
+
42
+ requestAnimationFrame((): void => {
43
+ timeout = setTimeout((): void => {
44
+ const currentStamp = Date.now();
45
+ const shouldSetState = currentStamp - frameState.lastStamp > 1000;
46
+ const newFramesCount = frameState.framesCount + 1;
47
+
48
+ // Updates FPS at most once per second
49
+ if (shouldSetState) {
50
+ const newValue = frameState.framesCount;
51
+ const totalCount = frameState.totalCount + 1;
52
+
53
+ // Math.min is used because values over 60 aren't really important
54
+ // Mean FPS is calculated incrementally instead of storing all values
55
+ const newMean = Math.min(
56
+ frameState.average + (newValue - frameState.average) / totalCount,
57
+ 60
58
+ );
59
+
60
+ setFrameState({
61
+ fps: frameState.framesCount,
62
+ lastStamp: currentStamp,
63
+ framesCount: 0,
64
+ average: newMean,
65
+ totalCount,
66
+ });
67
+ } else {
68
+ setFrameState({
69
+ ...frameState,
70
+ framesCount: newFramesCount,
71
+ });
72
+ }
73
+ }, 0);
74
+ });
75
+
76
+ return () => {
77
+ if (timeout) clearTimeout(timeout);
78
+ };
79
+ }, [frameState]);
80
+
81
+ return { average: frameState.average, fps: frameState.fps };
82
+ }
@@ -0,0 +1,81 @@
1
+ import { useEffect, useState, useRef } from 'react';
2
+ import { NativeModules, NativeEventEmitter, Platform } from 'react-native';
3
+
4
+ const FpsCounterModule = NativeModules.FpsCounter;
5
+
6
+ type UIFrameData = {
7
+ uiFps: number;
8
+ uiAverage: number;
9
+ isAvailable: boolean;
10
+ };
11
+
12
+ export type UIFPS = {
13
+ uiFps: number;
14
+ uiAverage: number;
15
+ /** Whether the native module is available */
16
+ isAvailable: boolean;
17
+ };
18
+
19
+ /**
20
+ * A custom hook that tracks native UI thread frames per second.
21
+ * Uses CADisplayLink on iOS and Choreographer on Android.
22
+ *
23
+ * @returns An object containing the current UI FPS, rolling average, and availability status
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * const { uiFps, uiAverage, isAvailable } = useUIFPSMetric();
28
+ * if (isAvailable) {
29
+ * console.log(`UI Thread: ${uiFps} FPS`);
30
+ * }
31
+ * ```
32
+ */
33
+ export function useUIFPSMetric(): UIFPS {
34
+ const [frameData, setFrameData] = useState<UIFrameData>({
35
+ uiFps: 0,
36
+ uiAverage: 0,
37
+ isAvailable: !!FpsCounterModule,
38
+ });
39
+ const eventEmitterRef = useRef<NativeEventEmitter | null>(null);
40
+
41
+ useEffect(() => {
42
+ // Handle case where native module is not available
43
+ if (!FpsCounterModule) {
44
+ if (__DEV__) {
45
+ console.warn(
46
+ '[react-native-fps-counter] Native module not found.\n' +
47
+ 'Make sure you have run:\n' +
48
+ ' - iOS: cd ios && pod install\n' +
49
+ ' - Android: Rebuild the app (npx react-native run-android)\n' +
50
+ 'If using Expo, you need to create a development build.'
51
+ );
52
+ }
53
+ return;
54
+ }
55
+
56
+ // Create event emitter
57
+ eventEmitterRef.current = new NativeEventEmitter(FpsCounterModule);
58
+
59
+ // Subscribe to FPS updates
60
+ const subscription = eventEmitterRef.current.addListener(
61
+ 'onUIFpsUpdate',
62
+ (data: { fps: number; average: number }) => {
63
+ setFrameData({
64
+ uiFps: Math.round(data.fps),
65
+ uiAverage: data.average,
66
+ isAvailable: true,
67
+ });
68
+ }
69
+ );
70
+
71
+ // Start monitoring
72
+ FpsCounterModule.startMonitoring();
73
+
74
+ return () => {
75
+ subscription.remove();
76
+ FpsCounterModule.stopMonitoring();
77
+ };
78
+ }, []);
79
+
80
+ return frameData;
81
+ }