1- import { ref , isRef , watch , onMounted , onUnmounted , Ref } from '../../api'
1+ import { ref , isRef , computed , onMounted , onUnmounted , Ref } from '../../api'
22
33type TFps = number | Ref < number >
44
5- const getFps = ( fps : TFps ) => ( isRef ( fps ) ? fps . value : fps )
5+ const getFps = ( fps : TFps ) => Number ( isRef ( fps ) ? fps . value : fps )
66const calcFpsInterval = ( fps : number ) => 1000 / fps
7+ const fpsLimit = 120
78
89export function useRafFn (
910 callback : Function ,
10- fps : TFps = 60 ,
11+ // Note: a value greater than 120 will disable the fps check logic
12+ // giving maximum precision and smoothness
13+ fps : TFps = fpsLimit + 1 ,
1114 runOnMount = true
1215) {
1316 const isRunningRef = ref ( false )
14- const fpsIntervalRef = ref ( calcFpsInterval ( getFps ( fps ) ) )
17+ // Using a computed here allows us to update fps
18+ // dynamically from user's input
19+ const fpsIntervalRef = computed ( ( ) => calcFpsInterval ( getFps ( fps ) ) )
20+
21+ let isPausedGuard = false
1522 let startTime = 0
1623 let timeNow = 0
17- let isPaused = false
18- let prevTime = 0
19- let timeLast = 0
20- function loop ( timeStamp : number ) {
24+ let timeWhenPaused = 0
25+ let timeDelta = 0
26+ const loop = ( timeStamp : number ) => {
2127 if ( ! startTime ) startTime = timeStamp
2228 if ( ! isRunningRef . value ) return
2329
24- if ( ! isPaused ) {
25- timeNow = timeStamp - startTime - prevTime
26- } else {
27- prevTime = timeStamp - startTime - timeNow
28- timeNow = timeStamp - startTime - prevTime
29- isPaused = false
30+ if ( isPausedGuard ) {
31+ // Save the time when we pause the loop so that later we can
32+ // adjust the time we return to the callback
33+ timeWhenPaused = timeStamp - startTime - timeNow
34+ isPausedGuard = false
3035 }
3136
32- // Run callback only on the given fps
33- if ( Math . ceil ( timeNow - timeLast ) > fpsIntervalRef . value ) {
37+ // Adjust timeNow to account for startTime and timeWhenPaused
38+ timeNow = timeStamp - startTime - timeWhenPaused
39+
40+ // Always run the callback if fps is greater than fpsLimit
41+ const callbackShouldAlwaysRun = getFps ( fps ) > fpsLimit
42+ if ( callbackShouldAlwaysRun ) {
43+ // Store timeDelta for future computations
44+ timeDelta = timeNow
3445 callback ( timeNow )
35- timeLast = timeNow
3646 }
3747
48+ // Run callback only when !callbackShouldAlwaysRun
49+ // and the given fps matches the lapsed time
50+ if ( ! callbackShouldAlwaysRun ) {
51+ const elapsedTime = Math . ceil ( timeNow - timeDelta )
52+ if ( elapsedTime > fpsIntervalRef . value ) {
53+ // Store timeDelta for future computations
54+ timeDelta = timeNow
55+ callback ( timeNow )
56+ }
57+ }
58+
59+ // Run loop again recursively
3860 requestAnimationFrame ( loop )
3961 }
4062
@@ -45,19 +67,8 @@ export function useRafFn(
4567
4668 const stop = ( ) => {
4769 isRunningRef . value = false
48- isPaused = true
49- }
50-
51- // Watch fps value since it could potentially be a ref and we may want
52- // to change the Raf speed from user's input
53- const updateFpsInterval = ( ) => {
54- // If fps is not a ref there is no point in updating it
55- if ( ! isRef ( fps ) ) return
56- watch ( fps , ( ) => {
57- fpsIntervalRef . value = calcFpsInterval ( getFps ( fps ) )
58- } )
70+ isPausedGuard = true
5971 }
60- updateFpsInterval ( )
6172
6273 onMounted ( ( ) => runOnMount && start ( ) )
6374 onUnmounted ( stop )
0 commit comments