39948-vm/frontend/src/hooks/useCanvasElapsedTime.ts
2026-03-29 16:03:25 +04:00

112 lines
3.0 KiB
TypeScript

/**
* useCanvasElapsedTime Hook
*
* Tracks elapsed time since page load for element visibility timing.
* Used in constructor.tsx to control element appear/disappear based on timing.
*/
import { useState, useEffect, useRef } from 'react';
interface UseCanvasElapsedTimeOptions {
/** Identifier for the current page (resets timer on change) */
pageId: string;
/** Whether the timer is active */
enabled?: boolean;
/** Update interval in milliseconds (default: 100ms) */
intervalMs?: number;
}
interface UseCanvasElapsedTimeResult {
/** Current elapsed time in seconds since page load */
elapsedSec: number;
/** Reset the elapsed time to zero */
reset: () => void;
/** Timestamp when the current page started (for external calculations) */
startedAt: number;
}
/**
* Hook to track elapsed time since page load.
* Resets when pageId changes. Used for element visibility timing.
*
* @example
* const { elapsedSec } = useCanvasElapsedTime({
* pageId: activePageId,
* enabled: !isLoading,
* });
*
* // Check if element should be visible
* const isVisible = elapsedSec >= element.appearDelaySec;
*/
export function useCanvasElapsedTime({
pageId,
enabled = true,
intervalMs = 100,
}: UseCanvasElapsedTimeOptions): UseCanvasElapsedTimeResult {
const [elapsedSec, setElapsedSec] = useState(0);
const startedAtRef = useRef<number>(Date.now());
// Reset timer when pageId changes
useEffect(() => {
if (typeof window === 'undefined') return;
if (!enabled || !pageId) {
setElapsedSec(0);
return;
}
startedAtRef.current = Date.now();
setElapsedSec(0);
const intervalId = window.setInterval(() => {
const elapsed = (Date.now() - startedAtRef.current) / 1000;
setElapsedSec(elapsed > 0 ? elapsed : 0);
}, intervalMs);
return () => window.clearInterval(intervalId);
}, [pageId, enabled, intervalMs]);
const reset = () => {
startedAtRef.current = Date.now();
setElapsedSec(0);
};
return {
elapsedSec,
reset,
startedAt: startedAtRef.current,
};
}
/**
* Check if an element should be visible based on timing settings.
*
* @param elapsedSec - Current elapsed time in seconds
* @param appearDelaySec - Delay before element appears (default: 0)
* @param appearDurationSec - Duration element is visible (null = infinite)
* @returns Whether the element should be visible
*/
export function isElementVisibleAtTime(
elapsedSec: number,
appearDelaySec?: number,
appearDurationSec?: number | null,
): boolean {
const delay = Number(appearDelaySec || 0);
// Not yet visible
if (elapsedSec < delay) return false;
// No duration limit - always visible after delay
if (appearDurationSec === null || appearDurationSec === undefined) {
return true;
}
const duration = Number(appearDurationSec);
if (!Number.isFinite(duration) || duration <= 0) return true;
// Check if still within visibility window
return elapsedSec <= delay + duration;
}
export default useCanvasElapsedTime;