Skip to content

Commit 6a83dd7

Browse files
author
Leroy Korterink
committed
Mimick requestAnimationFrame API and enable loop by default
1 parent de98f6a commit 6a83dd7

File tree

3 files changed

+35
-46
lines changed

3 files changed

+35
-46
lines changed

src/hooks/useAnimationLoop/useAnimationLoop.stories.tsx

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,11 @@ export default {
99
};
1010

1111
function DemoComponent(): ReactElement {
12-
const [delta, setDelta] = useState(0);
13-
const [currentTimestamp, setCurrentTimestamp] = useState(0);
14-
12+
const [timestamp, setTimestamp] = useState(0);
1513
const [isRunning, toggleIsRunning] = useToggle(true);
1614

17-
useAnimationLoop(() => {
18-
const timestamp = Date.now();
19-
20-
setDelta(timestamp - currentTimestamp);
21-
setCurrentTimestamp(timestamp);
15+
useAnimationLoop((_timestamp: DOMHighResTimeStamp) => {
16+
setTimestamp(_timestamp);
2217
}, isRunning);
2318

2419
return (
@@ -30,8 +25,7 @@ function DemoComponent(): ReactElement {
3025
<div className="card border-dark" data-ref="test-area">
3126
<div className="card-header">Test Area</div>
3227
<div className="card-body">
33-
<p>Current time: {currentTimestamp}</p>
34-
<p>Delta: {delta}</p>
28+
<p>Timestamp: {timestamp}</p>
3529

3630
<button
3731
type="button"

src/hooks/useAnimationLoop/useAnimationLoop.test.tsx

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@ describe('useAnimationLoop', () => {
99
});
1010
});
1111

12-
it('should not execute the callback function when enabled is not passed', async () => {
12+
it('should not execute the callback function when enabled is set to false', async () => {
1313
const spy = jest.fn();
14+
1415
renderHook(
1516
({ callback }) => {
16-
useAnimationLoop(callback);
17+
useAnimationLoop(callback, false);
1718
},
1819
{
1920
initialProps: {
@@ -29,35 +30,34 @@ describe('useAnimationLoop', () => {
2930

3031
it('should execute the callback function when useAnimationLoop is enabled', async () => {
3132
const spy = jest.fn();
33+
3234
renderHook(
33-
({ callback, enabled }) => {
34-
useAnimationLoop(callback, enabled);
35+
({ callback }) => {
36+
useAnimationLoop(callback);
3537
},
3638
{
3739
initialProps: {
3840
callback: spy,
39-
enabled: true,
4041
},
4142
},
4243
);
4344

44-
expect(spy).toBeCalledTimes(0);
4545
await waitFor(() => {
4646
expect(spy).toBeCalled();
4747
});
4848
});
4949

50-
it('should execute another callback function when it is passed to useAnimationLoop and not execute previously passed callback function', async () => {
50+
it('should not execute previous callback function when callback function is updated', async () => {
5151
const spyFirstRender = jest.fn();
5252
const spySecondRender = jest.fn();
53+
5354
const { rerender } = renderHook(
54-
({ callback, enabled }) => {
55-
useAnimationLoop(callback, enabled);
55+
({ callback }) => {
56+
useAnimationLoop(callback);
5657
},
5758
{
5859
initialProps: {
5960
callback: spyFirstRender,
60-
enabled: true,
6161
},
6262
},
6363
);
@@ -67,16 +67,18 @@ describe('useAnimationLoop', () => {
6767
expect(spySecondRender).toBeCalledTimes(0);
6868
});
6969

70-
rerender({ callback: spySecondRender, enabled: true });
70+
rerender({ callback: spySecondRender });
7171
const amountOfCalls = spyFirstRender.mock.calls.length;
72+
7273
await waitFor(() => {
7374
expect(spyFirstRender).toBeCalledTimes(amountOfCalls);
7475
expect(spySecondRender).toBeCalled();
7576
});
7677
});
7778

78-
it('should execute the callback function when useAnimationLoop is enabled on the first render and should not execute the callback function when useAnimationLoop is disabled on the second render', async () => {
79+
it('should execute the callback function when useAnimationLoop is enabled and should not execute the callback function when useAnimationLoop is disabled', async () => {
7980
const spy = jest.fn();
81+
8082
const { rerender } = renderHook(
8183
({ callback, enabled }) => {
8284
useAnimationLoop(callback, enabled);
@@ -89,13 +91,16 @@ describe('useAnimationLoop', () => {
8991
},
9092
);
9193

92-
waitFor(() => {
94+
await waitFor(() => {
9395
expect(spy).toBeCalled();
9496
});
9597

9698
rerender({ callback: spy, enabled: false });
97-
waitFor(() => {
98-
expect(spy).toBeCalledTimes(0);
99+
100+
const amountOfCalls = spy.mock.calls.length;
101+
102+
await waitFor(() => {
103+
expect(spy).toBeCalledTimes(amountOfCalls);
99104
});
100105
});
101106
});

src/hooks/useAnimationLoop/useAnimationLoop.ts

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,37 +8,27 @@ import { useRefValue } from '../useRefValue/useRefValue.js';
88
* @param callback - callback function with @param delta which represents time passed since last invocation
99
* @param enabled - boolean which is used to play and pause the requestAnimationFrame
1010
*/
11-
export function useAnimationLoop(callback: (delta: number) => void, enabled = false): void {
11+
export function useAnimationLoop(callback: FrameRequestCallback, enabled = true): void {
1212
const animationFrameRef = useRef(0);
13-
const lastTimeRef = useRef(0);
1413
const callbackRef = useRefValue(callback);
1514

16-
const tick = useCallback(
17-
(time: number): void => {
18-
const delta = time - lastTimeRef.current;
19-
lastTimeRef.current = time;
20-
21-
callbackRef.current?.(delta);
22-
15+
const tick = useCallback<FrameRequestCallback>(
16+
(time) => {
17+
callbackRef.current?.(time);
2318
animationFrameRef.current = requestAnimationFrame(tick);
2419
},
2520
[callbackRef],
2621
);
2722

28-
const play = useCallback(() => {
29-
lastTimeRef.current = performance.now();
30-
requestAnimationFrame(tick);
31-
}, [tick]);
32-
33-
const pause = useCallback(() => {
34-
cancelAnimationFrame(animationFrameRef.current);
35-
}, []);
36-
3723
useEffect(() => {
3824
if (enabled) {
39-
play();
25+
requestAnimationFrame(tick);
26+
} else {
27+
cancelAnimationFrame(animationFrameRef.current);
4028
}
4129

42-
return pause;
43-
}, [enabled, pause, play]);
30+
return () => {
31+
cancelAnimationFrame(animationFrameRef.current);
32+
};
33+
}, [enabled, tick]);
4434
}

0 commit comments

Comments
 (0)