a1bda3e484
AI Code Review / AI Code Review (pull_request) Failing after 0s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 32s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 47s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m16s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m16s
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 / PR Build Worker Image (pull_request) Successful in 32s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 57s
Preview Deploy / Deploy Preview Environment (pull_request) Failing after 15s
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 54s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 3m3s
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 28s
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 2m14s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 2m36s
Extract 4 business hooks from EditingPlanner.tsx: - useEditorDrawers (78行): 11个抽屉开关 + 目标ID + 快捷打开 - usePlaybackControl (56行): 播放/暂停、rAF帧推进、缩放、seek - useClipOperations (231行): 片段CRUD、重排、裁剪、分割、转场、调速、TTS、配音 - useTemplateManagement (459行): 模板列表/详情/计划加载、保存、模式切换、筛选搜索 主文件: 997→415行(-58%)
57 lines
1.5 KiB
TypeScript
57 lines
1.5 KiB
TypeScript
import { useState, useCallback, useEffect, useRef } from "react"
|
|
|
|
/**
|
|
* 播放控制 Hook
|
|
* 播放/暂停、rAF 帧推进、时间线缩放、seek
|
|
*/
|
|
export const usePlaybackControl = (totalDuration: number) => {
|
|
const [isPlaying, setIsPlaying] = useState(false)
|
|
const [currentTime, setCurrentTime] = useState(0)
|
|
const [pixelsPerSecond, setPixelsPerSecond] = useState(40)
|
|
const prevFrameTimeRef = useRef<number | null>(null)
|
|
|
|
/** 播放头跳转 */
|
|
const handleSeek = useCallback((time: number) => {
|
|
setCurrentTime(Math.max(0, time))
|
|
}, [])
|
|
|
|
/** 轨道缩放 */
|
|
const handleZoomChange = useCallback((pps: number) => {
|
|
setPixelsPerSecond(pps)
|
|
}, [])
|
|
|
|
/** rAF 帧推进 — 播放时平滑更新播放头位置 */
|
|
useEffect(() => {
|
|
if (!isPlaying) {
|
|
prevFrameTimeRef.current = null
|
|
return
|
|
}
|
|
let rafId: number
|
|
const tick = (timestamp: number) => {
|
|
if (prevFrameTimeRef.current !== null) {
|
|
const delta = (timestamp - prevFrameTimeRef.current) / 1000
|
|
setCurrentTime((prev) => {
|
|
const next = prev + delta
|
|
return next >= totalDuration ? totalDuration : next
|
|
})
|
|
}
|
|
prevFrameTimeRef.current = timestamp
|
|
rafId = requestAnimationFrame(tick)
|
|
}
|
|
rafId = requestAnimationFrame(tick)
|
|
return () => {
|
|
cancelAnimationFrame(rafId)
|
|
prevFrameTimeRef.current = null
|
|
}
|
|
}, [isPlaying, totalDuration])
|
|
|
|
return {
|
|
isPlaying,
|
|
setIsPlaying,
|
|
currentTime,
|
|
pixelsPerSecond,
|
|
handleSeek,
|
|
handleZoomChange,
|
|
}
|
|
}
|