diff --git a/apps/web/src/pages/editing-planner/components/timeline/hooks/usePlayheadDrag.ts b/apps/web/src/pages/editing-planner/components/timeline/hooks/usePlayheadDrag.ts new file mode 100644 index 000000000..1dbf9fbf3 --- /dev/null +++ b/apps/web/src/pages/editing-planner/components/timeline/hooks/usePlayheadDrag.ts @@ -0,0 +1,76 @@ +import { useState, useCallback, useEffect, useRef } from "react" + +interface UsePlayheadDragOptions { + /** 当前播放时间(秒) */ + currentTime: number + /** 缩放:每秒像素数 */ + pps: number + /** 总时长(秒) */ + totalDuration: number + /** 播放头跳转回调 */ + onSeek?: (time: number) => void +} + +/** + * 播放头拖拽 Hook + * 封装播放头的 mousedown/mousemove/mouseup 拖拽逻辑 + */ +export const usePlayheadDrag = ({ + pps, + totalDuration, + onSeek, +}: UsePlayheadDragOptions) => { + const [playheadDragging, setPlayheadDragging] = useState(false) + const trackRef = useRef(null) + + /* ── 播放头拖拽全局 mousemove/mouseup ── */ + useEffect(() => { + if (!playheadDragging) return + + const handleMouseMove = (e: MouseEvent) => { + const trackEl = trackRef.current + if (!trackEl) return + const rect = trackEl.getBoundingClientRect() + const x = e.clientX - rect.left + trackEl.scrollLeft + const time = Math.max(0, Math.min(x / pps, totalDuration)) + onSeek?.(Math.round(time * 10) / 10) + } + + const handleMouseUp = () => { + setPlayheadDragging(false) + } + + document.addEventListener("mousemove", handleMouseMove) + document.addEventListener("mouseup", handleMouseUp) + return () => { + document.removeEventListener("mousemove", handleMouseMove) + document.removeEventListener("mouseup", handleMouseUp) + } + }, [playheadDragging, pps, totalDuration, onSeek]) + + /* ── 播放头拖拽开始 ── */ + const handlePlayheadMouseDown = useCallback((e: React.MouseEvent) => { + e.preventDefault() + e.stopPropagation() + setPlayheadDragging(true) + }, []) + + /* ── 标尺点击跳转播放头 ── */ + const handleRulerClick = useCallback( + (e: React.MouseEvent) => { + const rect = e.currentTarget.getBoundingClientRect() + const x = e.clientX - rect.left + const time = Math.max(0, Math.min(x / pps, totalDuration)) + onSeek?.(Math.round(time * 10) / 10) + }, + [pps, totalDuration, onSeek], + ) + + return { + trackRef, + playheadDragging, + handlePlayheadMouseDown, + handleRulerClick, + } +} +