2546961Plate mechanics lab: Kirchhoff-Love plates solved by numbl in the browserJeremy 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((i: number) => setIndexState(Math.max(0, Math.min(nf - 1, i))), [nf]);
26 return { index, setIndex, playing, setPlaying };
27}
29export interface FramePlayerProps extends FramePlayerState {
30 times: Float64Array;
31 /** Show time in these units */
32 timeScale?: number;
33 timeUnit?: string;
34}
36export function FramePlayer({ index, setIndex, playing, setPlaying, times, timeScale = 1000, timeUnit = "ms" }: FramePlayerProps) {
37 const nf = times.length;
38 const t = times[Math.min(index, nf - 1)] ?? 0;
39 return (
40 <div className="pl-player">
41 <button className="pl-btn" onClick={() => setPlaying(!playing)} disabled={nf <= 1} aria-label={playing ? "pause" : "play"}>
42 {playing ? "Pause" : "Play"}
43 </button>
44 <input type="range" min={0} max={Math.max(0, nf - 1)} value={Math.min(index, nf - 1)} onChange={(e) => setIndex(Number(e.target.value))} aria-label="frame" />
45 <span className="pl-mono">
46 {(t * timeScale).toFixed(2)} {timeUnit}
47 </span>
48 <span className="pl-muted pl-small">
49 frame {Math.min(index, nf - 1) + 1}/{nf}
50 </span>
51 </div>
52 );
53}