import { memo } from "react"; import type { AbLoop } from "../types.ts"; interface ProgressBarProps { progress: number; onSeek: (ratio: number) => void; abLoop?: AbLoop; duration?: number; } export const ProgressBar = memo(function ProgressBar({ progress, onSeek, abLoop, duration = 0, }: ProgressBarProps) { const seekFromClientX = (clientX: number, element: HTMLElement): void => { const rect = element.getBoundingClientRect(); if (rect.width <= 0) return; onSeek((clientX - rect.left) / rect.width); }; const hasAbLoop = Boolean( abLoop && (abLoop.a !== null || abLoop.b !== null) && duration > 0, ); const startRatio = hasAbLoop && abLoop ? Math.max(0, Math.min(1, (abLoop.a ?? 0) / duration)) : 0; const endRatio = hasAbLoop && abLoop ? Math.max(startRatio, Math.min(1, (abLoop.b ?? duration) / duration)) : 1; return (
{ seekFromClientX(e.clientX, e.currentTarget as HTMLElement); }} onPointerDown={(e) => { if (e.pointerType === "mouse" && e.button !== 0) return; e.currentTarget.setPointerCapture(e.pointerId); seekFromClientX(e.clientX, e.currentTarget); }} onPointerMove={(e) => { if (!e.currentTarget.hasPointerCapture(e.pointerId)) return; seekFromClientX(e.clientX, e.currentTarget); }} onPointerUp={(e) => { if (e.currentTarget.hasPointerCapture(e.pointerId)) { e.currentTarget.releasePointerCapture(e.pointerId); } }} onPointerCancel={(e) => { if (e.currentTarget.hasPointerCapture(e.pointerId)) { e.currentTarget.releasePointerCapture(e.pointerId); } }} > {/* 轨道主体 */}
{/* AB 循环区间底色高亮 */} {hasAbLoop && (
)} {/* 播放进度填充 */}
{/* AB 循环区间顶部与底部亮边 */} {hasAbLoop && (
)}
{/* A / B 端点指示标记 */} {hasAbLoop && ( <>
A
B
)}
); });