"use client"; import { useState } from "react"; import { toast } from "sonner"; import type { DirectorScene } from "@/lib/video-canvas/director-scene"; import { recordObjectFrames } from "@/lib/video-canvas/director-object-tracks"; export function DirectorObjectTimeline({ scene, time, selected, disabled, preview, auto, onAuto, onChange, onSeek, onEdit, onSelect, }: { scene: DirectorScene; time: number; selected: string | null; disabled: boolean; preview: boolean; auto: boolean; onAuto: (v: boolean) => void; onChange: (s: DirectorScene) => void; onSeek: (t: number) => void; onEdit: () => void; onSelect: (id: string) => void; }) { const [unit, setUnit] = useState("seconds"), [zoom, setZoom] = useState(1); const factor = unit === "frames" ? 30 : 1, track = selected ? scene.objectTracks?.[selected] : undefined; const button = "rounded border border-white/20 px-2 py-1 text-xs disabled:opacity-40", field = "rounded bg-[#303030] p-1 text-xs"; return (
对象多轨 · 与相机同步
{(time * factor).toFixed(unit === "frames" ? 0 : 2)} /{" "} {(scene.motion.duration * factor).toFixed(unit === "frames" ? 0 : 2)}{" "} {unit === "frames" ? "帧" : "秒"}
onSeek(Number(e.target.value))} /> {scene.objects.map((o) => { const t = scene.objectTracks?.[o.id]; return (
{t?.keyframes.map((k, i) => ( ))}
); })}
{track && selected ? (
{track.keyframes.map((k, i) => ( ))}
) : null}

拖动播放头预览全部对象;点击“编辑当前时间对象”后调整场景对象,再记录关键帧。真实视频导出使用相同轨道。

); }