112 lines
3.0 KiB
TypeScript
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;
|