From 52f003f25ab8d3124ddf4880c44f874206d7f988 Mon Sep 17 00:00:00 2001 From: gantiro Date: Tue, 15 Jul 2025 22:56:56 +0700 Subject: add comment for hooks --- apps/web/src/hooks/use-timeline-playhead.ts | 40 +++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) (limited to 'apps/web/src/hooks/use-timeline-playhead.ts') diff --git a/apps/web/src/hooks/use-timeline-playhead.ts b/apps/web/src/hooks/use-timeline-playhead.ts index 8aee5b1f..de3f8ccf 100644 --- a/apps/web/src/hooks/use-timeline-playhead.ts +++ b/apps/web/src/hooks/use-timeline-playhead.ts @@ -11,6 +11,13 @@ interface UseTimelinePlayheadProps { rulerScrollRef: React.RefObject; } +/** + * useTimelinePlayhead + * + * Custom hook to manage playhead (scrubbing) logic for a timeline editor. + * Handles mouse interaction for timeline ruler, calculates time from mouse, + * and enables smooth scrubbing without unnecessary re-renders. + */ export function useTimelinePlayhead({ currentTime, duration, @@ -19,17 +26,35 @@ export function useTimelinePlayhead({ rulerRef, rulerScrollRef, }: UseTimelinePlayheadProps) { + // Get current project info (especially FPS) from global store const { activeProject } = useProjectStore(); + // Ref to track if currently scrubbing const isScrubbingRef = useRef(false); + + // Ref to hold the playhead time while scrubbing (does not trigger re-render) const scrubTimeRef = useRef(null); + + // State to force re-render when scrubbing ends const [_, forceRerender] = useState(0); + /** + * Determines the actual playhead position: + * - If scrubbing, use the value in scrubTimeRef + * - Otherwise, use the currentTime (controlled by external state) + */ const playheadPosition = isScrubbingRef.current && scrubTimeRef.current !== null ? scrubTimeRef.current : currentTime; + /** + * Calculate timeline time (in seconds) based on mouse X position. + * - Gets bounding rect of the ruler + * - Adjusts for scroll position if ruler is scrollable + * - Converts X pixel offset to seconds using current zoom level + * - Snaps to nearest frame using FPS + */ const getTimeFromMouse = useCallback( (e: MouseEvent | React.MouseEvent) => { const ruler = rulerRef.current; @@ -43,6 +68,7 @@ export function useTimelinePlayhead({ const scrollLeft = scrollArea.scrollLeft; const x = e.clientX - rect.left + scrollLeft; + // Calculate how many pixels represent one second, depending on zoom const pixelsPerSecond = 50 * zoomLevel; const rawTime = Math.max(0, Math.min(duration, x / pixelsPerSecond)); const time = snapTimeToFrame(rawTime, activeProject?.fps || 30); @@ -51,6 +77,13 @@ export function useTimelinePlayhead({ [rulerRef, rulerScrollRef, duration, zoomLevel, activeProject?.fps] ); + /** + * Handle mouse down event on the ruler: + * - Starts scrubbing and updates playhead immediately + * - Registers mousemove/mouseup events to allow scrubbing + * - Updates time as mouse moves and seeks to new time + * - Cleans up listeners and triggers re-render when scrubbing ends + */ const handleRulerMouseDown = useCallback( (e: React.MouseEvent) => { e.preventDefault(); @@ -61,6 +94,7 @@ export function useTimelinePlayhead({ scrubTimeRef.current = time; seek(time); + // Mouse move handler: update playhead and seek to new time const onMouseMove = (e: MouseEvent) => { const t = getTimeFromMouse(e); if (t !== scrubTimeRef.current) { @@ -69,6 +103,7 @@ export function useTimelinePlayhead({ } }; + // Mouse up handler: stop scrubbing, cleanup, and force re-render const onMouseUp = () => { isScrubbingRef.current = false; window.removeEventListener("mousemove", onMouseMove); @@ -76,6 +111,7 @@ export function useTimelinePlayhead({ forceRerender((v) => v + 1); }; + // Attach listeners to window for drag outside the ruler area window.addEventListener("mousemove", onMouseMove); window.addEventListener("mouseup", onMouseUp); }, @@ -83,7 +119,7 @@ export function useTimelinePlayhead({ ); return { - playheadPosition, - handleRulerMouseDown, + playheadPosition, // Current playhead position (real-time while scrubbing) + handleRulerMouseDown, // Attach to the ruler's onMouseDown }; } -- cgit v1.3.1