Files
xiaoxia-saas/apps/web/src/pages/editing-planner/components/SpeedPanel.tsx
T
xiaoxia 0fcb77b991
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Successful in 1m6s
CI Build & Deploy Pipeline / Build Staging Web Image (push) Successful in 1m49s
CI Build & Deploy Pipeline / Build Production API Image (push) Has been skipped
CI Build & Deploy Pipeline / Build Production Web Image (push) Has been skipped
CI Build & Deploy Pipeline / Build Production Worker Image (push) Has been skipped
CI Build & Deploy Pipeline / Deploy Production (push) Has been skipped
CI Build & Deploy Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Validate Code Quality And Tests (push) Successful in 3m1s
CI/CD Pipeline / Unit Tests (push) Successful in 3m20s
CI/CD Pipeline / Integration Tests (push) Successful in 1m29s
CI Build & Deploy Pipeline / Build Staging API Image (push) Successful in 7m4s
CI Build & Deploy Pipeline / Build Staging Worker Image (push) Successful in 7m54s
CI Build & Deploy Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 45s
CI Build & Deploy Pipeline / Staging E2E Tests (push) Failing after 2m50s
CI Build & Deploy Pipeline / Staging API Integration Tests (push) Successful in 3m28s
ci: Prettier纳入两层防御体系 (#520)
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-07-18 18:08:19 +08:00

152 lines
4.4 KiB
TypeScript

/**
* 片段调速面板 — Drawer 形式
* 速度滑块(0.25x ~ 4x)+ 预设快捷按钮 + 音调修正开关
* 支持应用到当前片段 / 所有片段
*/
import React, { useCallback } from "react"
import { Drawer, Slider } from "antd"
import type { SpeedConfig } from "../types"
import { DEFAULT_SPEED } from "../types"
/* ──────────── 预设速度 ──────────── */
const SPEED_PRESETS: { rate: number; label: string }[] = [
{ rate: 0.5, label: "0.5x" },
{ rate: 1.0, label: "1x" },
{ rate: 1.5, label: "1.5x" },
{ rate: 2.0, label: "2x" },
]
/* ──────────── Props ──────────── */
interface SpeedPanelProps {
open: boolean
onClose: () => void
/** 当前片段调速配置 */
config: SpeedConfig
onChange: (config: SpeedConfig) => void
/** 应用到所有片段 */
onApplyAll?: (config: SpeedConfig) => void
}
const SpeedPanel: React.FC<SpeedPanelProps> = ({ open, onClose, config, onChange, onApplyAll }) => {
/* ── 修改速度 ── */
const handleChangeRate = useCallback(
(rate: number) => {
onChange({ ...config, rate })
},
[config, onChange],
)
/* ── 切换音调修正 ── */
const handleTogglePitch = useCallback(() => {
onChange({ ...config, pitchCorrection: !config.pitchCorrection })
}, [config, onChange])
/* ── 选择预设 ── */
const handlePreset = useCallback(
(rate: number) => {
onChange({ ...config, rate })
},
[config, onChange],
)
/* ── 应用到所有片段 ── */
const handleApplyAll = useCallback(() => {
onApplyAll?.(config)
}, [config, onApplyAll])
/* ── 重置 ── */
const handleReset = useCallback(() => {
onChange({ ...DEFAULT_SPEED })
}, [onChange])
/* ── 速度描述文字 ── */
const speedLabel =
config.rate < 1
? "慢速(慢动作)"
: config.rate === 1
? "原速"
: config.rate < 2
? "快速"
: "极速"
return (
<Drawer
title="⚡ 片段调速"
placement="right"
width={380}
open={open}
onClose={onClose}
className="speed-panel-drawer"
>
{/* ── 速度滑块 ── */}
<div className="sp-speed-section">
<div className="sp-speed-header">
<span className="sp-speed-label">播放速度</span>
<span className="sp-speed-value">{config.rate.toFixed(2)}x</span>
</div>
<Slider
min={0.25}
max={4.0}
step={0.05}
value={config.rate}
onChange={handleChangeRate}
tooltip={{ formatter: (v) => `${(v as number).toFixed(2)}x` }}
/>
<div className="sp-speed-marks">
<span>0.25x</span>
<span>1x</span>
<span>2x</span>
<span>4x</span>
</div>
<div className="sp-speed-desc">{speedLabel}</div>
</div>
{/* ── 预设快捷按钮 ── */}
<div className="sp-presets">
<div className="sp-presets-label">快捷预设</div>
<div className="sp-presets-row">
{SPEED_PRESETS.map((p) => (
<button
key={p.rate}
className={`sp-preset-btn${Math.abs(config.rate - p.rate) < 0.01 ? " active" : ""}`}
onClick={() => handlePreset(p.rate)}
>
{p.label}
</button>
))}
</div>
</div>
{/* ── 音调修正开关 ── */}
<div className="sp-pitch-section">
<div className="sp-pitch-info">
<span className="sp-pitch-label">音调修正</span>
<span className="sp-pitch-desc">
{config.pitchCorrection ? "变速不变调(推荐)" : "变速同时变调"}
</span>
</div>
<div
className={`ep-toggle${config.pitchCorrection ? " active" : ""}`}
onClick={handleTogglePitch}
>
<div className="ep-toggle-knob" />
</div>
</div>
{/* ── 底部操作 ── */}
<div className="sp-footer">
<button className="sp-reset-btn" onClick={handleReset}>
重置原速
</button>
{onApplyAll && (
<button className="sp-apply-all-btn" onClick={handleApplyAll}>
应用到所有片段
</button>
)}
</div>
</Drawer>
)
}
export default SpeedPanel