2004981Plate dynamics lab 2: a hammer on a hung plate by Rayleigh-Ritz and by Mindlin-Reissner finite elementsJeremy Magland 1import { useCallback, useEffect, useRef, useState } from "react";
3export interface FramePlayerState {
4 index: number;
5 setIndex: (i: number) => void;
6 playing: boolean;
7 setPlaying: (p: boolean) => void;
8}
10/** Advance a frame index at a fixed rate while playing. */
11export function useFramePlayer(nf: number, fps = 20, autoplay = false): FramePlayerState {
12 const [index, setIndexState] = useState(0);
13 const [playing, setPlaying] = useState(autoplay);
14 const timer = useRef<number | null>(null);
15 useEffect(() => {
16 if (index >= nf) setIndexState(0);
17 }, [nf, index]);
18 useEffect(() => {
19 if (!playing || nf <= 1) return;
20 timer.current = window.setInterval(() => setIndexState((i) => (i + 1) % nf), 1000 / fps);
21 return () => {
22 if (timer.current !== null) window.clearInterval(timer.current);
23 };
24 }, [playing, nf, fps]);
25 const setIndex = useCallback(
26 (i: number) => setIndexState(Math.max(0, Math.min(nf - 1, i))),
27 [nf],
28 );
29 return { index, setIndex, playing, setPlaying };
30}
32export interface FramePlayerProps extends FramePlayerState {
33 times: Float64Array;
34 /** Show time in these units */
35 timeScale?: number;
36 timeUnit?: string;
37}
39export function FramePlayer({
40 index,
41 setIndex,
42 playing,
43 setPlaying,
44 times,
45 timeScale = 1000,
46 timeUnit = "ms",
47}: FramePlayerProps) {
48 const nf = times.length;
49 const t = times[Math.min(index, nf - 1)] ?? 0;
50 return (
51 <div className="pl-player">
52 <button
53 className="pl-btn"
54 onClick={() => setPlaying(!playing)}
55 disabled={nf <= 1}
56 aria-label={playing ? "pause" : "play"}
57 >
58 {playing ? "Pause" : "Play"}
59 </button>
60 <input
61 type="range"
62 min={0}
63 max={Math.max(0, nf - 1)}
64 value={Math.min(index, nf - 1)}
65 onChange={(e) => setIndex(Number(e.target.value))}
66 aria-label="frame"
67 />
68 <span className="pl-mono">
69 {(t * timeScale).toFixed(2)} {timeUnit}
70 </span>
71 <span className="pl-muted pl-small">
72 frame {Math.min(index, nf - 1) + 1}/{nf}
73 </span>
74 </div>
75 );
76}