Files
DndGamePlayer/app/renderer/shared/playerToken/useLiveDragBroadcast.ts
T
2026-08-18 14:42:06 +08:00

96 lines
2.7 KiB
TypeScript

import { useCallback, useEffect, useRef } from 'react';
/** Во время drag шлёт onMove не чаще одного раза за кадр (для live-синхронизации презентации). */
export function useLiveDragBroadcast(
onMove: ((id: string, nx: number, ny: number) => void) | undefined,
id: string,
): {
schedule: (nx: number, ny: number) => void;
flush: (nx: number, ny: number) => void;
} {
const rafRef = useRef(0);
const pendingRef = useRef<{ nx: number; ny: number } | null>(null);
const onMoveRef = useRef(onMove);
onMoveRef.current = onMove;
const idRef = useRef(id);
idRef.current = id;
useEffect(
() => () => {
if (rafRef.current) cancelAnimationFrame(rafRef.current);
},
[],
);
const schedule = useCallback((nx: number, ny: number) => {
if (!onMoveRef.current) return;
pendingRef.current = { nx, ny };
if (rafRef.current) return;
rafRef.current = requestAnimationFrame(() => {
rafRef.current = 0;
const pending = pendingRef.current;
const cb = onMoveRef.current;
if (!pending || !cb) return;
cb(idRef.current, pending.nx, pending.ny);
});
}, []);
const flush = useCallback((nx: number, ny: number) => {
if (rafRef.current) {
cancelAnimationFrame(rafRef.current);
rafRef.current = 0;
}
pendingRef.current = null;
onMoveRef.current?.(idRef.current, nx, ny);
}, []);
return { schedule, flush };
}
/** Во время resize шлёт onResize не чаще одного раза за кадр. */
export function useLiveResizeBroadcast(
onResize: ((id: string, sizeN: number) => void) | undefined,
id: string,
): {
schedule: (sizeN: number) => void;
flush: (sizeN: number) => void;
} {
const rafRef = useRef(0);
const pendingRef = useRef<number | null>(null);
const onResizeRef = useRef(onResize);
onResizeRef.current = onResize;
const idRef = useRef(id);
idRef.current = id;
useEffect(
() => () => {
if (rafRef.current) cancelAnimationFrame(rafRef.current);
},
[],
);
const schedule = useCallback((sizeN: number) => {
if (!onResizeRef.current) return;
pendingRef.current = sizeN;
if (rafRef.current) return;
rafRef.current = requestAnimationFrame(() => {
rafRef.current = 0;
const pending = pendingRef.current;
const cb = onResizeRef.current;
if (pending === null || !cb) return;
cb(idRef.current, pending);
});
}, []);
const flush = useCallback((sizeN: number) => {
if (rafRef.current) {
cancelAnimationFrame(rafRef.current);
rafRef.current = 0;
}
pendingRef.current = null;
onResizeRef.current?.(idRef.current, sizeN);
}, []);
return { schedule, flush };
}