fix: prettier format
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1m12s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 2m17s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 2m9s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m0s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m27s
AI Code Review / AI Code Review (pull_request) Failing after 1m53s
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m17s
CI/CD Pipeline / PR Build API Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 4m59s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m28s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 2m45s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 3m4s
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled

This commit is contained in:
2026-07-30 09:58:35 +08:00
parent 5ab2732284
commit f710f5935b
@@ -1,75 +1,71 @@
import { useState, useCallback, useEffect, useRef } from "react";
import { useState, useCallback, useEffect, useRef } from "react"
interface UsePlayheadDragOptions {
/** 当前播放时间(秒) */
currentTime: number;
currentTime: number
/** 缩放:每秒像素数 */
pps: number;
pps: number
/** 总时长(秒) */
totalDuration: number;
totalDuration: number
/** 播放头跳转回调 */
onSeek?: (time: number) => void;
onSeek?: (time: number) => void
}
/**
* 播放头拖拽 Hook
* 封装播放头的 mousedown/mousemove/mouseup 拖拽逻辑
*/
export const usePlayheadDrag = ({
pps,
totalDuration,
onSeek,
}: UsePlayheadDragOptions) => {
const [playheadDragging, setPlayheadDragging] = useState(false);
const trackRef = useRef<HTMLDivElement>(null);
export const usePlayheadDrag = ({ pps, totalDuration, onSeek }: UsePlayheadDragOptions) => {
const [playheadDragging, setPlayheadDragging] = useState(false)
const trackRef = useRef<HTMLDivElement>(null)
/* ── 播放头拖拽全局 mousemove/mouseup ── */
useEffect(() => {
if (!playheadDragging) return;
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 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);
};
setPlayheadDragging(false)
}
document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("mouseup", handleMouseUp);
document.addEventListener("mousemove", handleMouseMove)
document.addEventListener("mouseup", handleMouseUp)
return () => {
document.removeEventListener("mousemove", handleMouseMove);
document.removeEventListener("mouseup", handleMouseUp);
};
}, [playheadDragging, pps, totalDuration, onSeek]);
document.removeEventListener("mousemove", handleMouseMove)
document.removeEventListener("mouseup", handleMouseUp)
}
}, [playheadDragging, pps, totalDuration, onSeek])
/* ── 播放头拖拽开始 ── */
const handlePlayheadMouseDown = useCallback((e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
setPlayheadDragging(true);
}, []);
e.preventDefault()
e.stopPropagation()
setPlayheadDragging(true)
}, [])
/* ── 标尺点击跳转播放头 ── */
const handleRulerClick = useCallback(
(e: React.MouseEvent<HTMLDivElement>) => {
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);
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,
};
};
}
}