feat: 优化时间轴
This commit is contained in:
@@ -53,7 +53,12 @@ import {
|
||||
type CameraKeyframeValue,
|
||||
type Timeline,
|
||||
} from "../schema/timeline";
|
||||
import { getTrajectoryStartTime, shiftTrajectoryClip } from "../schema/trajectoryMotion";
|
||||
import {
|
||||
TRAJECTORY_POINT_MIN_GAP,
|
||||
getTrajectoryPointTimes,
|
||||
getTrajectoryStartTime,
|
||||
shiftTrajectoryClip,
|
||||
} from "../schema/trajectoryMotion";
|
||||
|
||||
export type TransformMode = "translate" | "rotate" | "scale";
|
||||
|
||||
@@ -159,8 +164,13 @@ export interface DirectorActions {
|
||||
setTrajectoryPointTimes: (objectId: string, times: number[]) => void;
|
||||
/** 整体平移人物轨迹片段(拖轨迹条本体):片段起点 + 所有轨迹点时间同步平移 */
|
||||
setTrajectoryClipStart: (objectId: string, startTime: number) => void;
|
||||
/** 修剪人物轨迹片段起点(拖轨迹条左端把手):只挪片段起点,轨迹点与停留结束时间都不动 */
|
||||
setTrajectoryClipTrimStart: (objectId: string, startTime: number) => void;
|
||||
/** 设置人物轨迹片段的结束时间(停留时长):角色走完最后一点后原地停留到这一刻,拖条右端把手时写入 */
|
||||
setTrajectoryClipEnd: (objectId: string, endTime: number) => void;
|
||||
removeTrajectoryPoint: (objectId: string, pointId: string) => void;
|
||||
selectTrajectoryPoint: (objectId: string, pointId: string) => void;
|
||||
/** 选中某个轨迹点;传 null 表示取消选中(点到轨道空白处 / 面板其它地方时清掉轨迹关键帧的选中态) */
|
||||
selectTrajectoryPoint: (objectId: string | null, pointId: string | null) => void;
|
||||
updateScene: (patch: Partial<SceneSettings>) => void;
|
||||
removePanoramaAsset: () => void;
|
||||
removeImportedAsset: (assetId: string) => void;
|
||||
@@ -1763,21 +1773,75 @@ export const useDirectorStore = create<DirectorStore>((set, get) => {
|
||||
|
||||
// 片段刚性平移:起点夹在时间轴内,所有轨迹点跟着走同样的量。
|
||||
// 点的时间在这里落成显式值,避免默认推导把片段「压缩」而不是「平移」。
|
||||
const currentStart = getTrajectoryStartTime(item, state.project.timeline.duration);
|
||||
const { startTime: nextStart, times } = shiftTrajectoryClip(
|
||||
points,
|
||||
state.project.timeline.duration,
|
||||
getTrajectoryStartTime(item, state.project.timeline.duration),
|
||||
currentStart,
|
||||
startTime
|
||||
);
|
||||
const delta = nextStart - currentStart;
|
||||
// 停留结束时间(trajectoryEndTime)同样随片段刚性平移,保持与轨迹点的相对关系
|
||||
const nextEndTime =
|
||||
typeof item.trajectoryEndTime === "number"
|
||||
? Math.min(Math.max(item.trajectoryEndTime + delta, 0), state.project.timeline.duration)
|
||||
: item.trajectoryEndTime;
|
||||
|
||||
return {
|
||||
...item,
|
||||
trajectoryStartTime: nextStart,
|
||||
trajectoryEndTime: nextEndTime,
|
||||
trajectoryPoints: points.map((point, index) => ({ ...point, time: times[index] })),
|
||||
};
|
||||
}),
|
||||
},
|
||||
})),
|
||||
setTrajectoryClipTrimStart: (objectId, startTime) =>
|
||||
commitMutation((state) => ({
|
||||
...state,
|
||||
project: {
|
||||
...state.project,
|
||||
objects: updateObjectById(state.project.objects, objectId, (item) => {
|
||||
const points = item.trajectoryPoints ?? [];
|
||||
if (points.length === 0) return item;
|
||||
|
||||
const duration = state.project.timeline.duration;
|
||||
const currentStart = getTrajectoryStartTime(item, duration);
|
||||
const times = getTrajectoryPointTimes(points, duration, currentStart);
|
||||
const firstPointTime = times[0] ?? 0;
|
||||
// 起点最多挪到「第一个轨迹点再往前一个最小间隔」处 —— 不能越过第一个点,
|
||||
// 否则那一列点就落在片段起点之前,和「起点之前原地等待」的语义冲突。
|
||||
const upper = Math.max(firstPointTime - TRAJECTORY_POINT_MIN_GAP, 0);
|
||||
const nextStart = Math.min(Math.max(startTime, 0), upper);
|
||||
|
||||
return {
|
||||
...item,
|
||||
trajectoryStartTime: nextStart,
|
||||
// 把点的时间落成显式值:这些点不该跟着起点走,否则就退化成整段平移而不是修剪起点。
|
||||
// (没有显式 time 的点会按「起点 → 时间轴末尾」重新推导,改起点会连点一起拖走。)
|
||||
trajectoryPoints: points.map((point, index) => ({ ...point, time: times[index] })),
|
||||
};
|
||||
}),
|
||||
},
|
||||
})),
|
||||
setTrajectoryClipEnd: (objectId, endTime) =>
|
||||
commitMutation((state) => ({
|
||||
...state,
|
||||
project: {
|
||||
...state.project,
|
||||
objects: updateObjectById(state.project.objects, objectId, (item) => {
|
||||
const points = item.trajectoryPoints ?? [];
|
||||
if (points.length === 0) return item;
|
||||
|
||||
const duration = state.project.timeline.duration;
|
||||
const times = getTrajectoryPointTimes(points, duration, getTrajectoryStartTime(item, duration));
|
||||
const lastPointTime = times.length > 0 ? times[times.length - 1] : 0;
|
||||
const nextEnd = Math.min(Math.max(endTime, lastPointTime), duration);
|
||||
|
||||
return { ...item, trajectoryEndTime: nextEnd };
|
||||
}),
|
||||
},
|
||||
})),
|
||||
removeTrajectoryPoint: (objectId, pointId) =>
|
||||
commitMutation((state) => ({
|
||||
...state,
|
||||
@@ -1795,6 +1859,11 @@ export const useDirectorStore = create<DirectorStore>((set, get) => {
|
||||
})),
|
||||
selectTrajectoryPoint: (objectId, pointId) =>
|
||||
commitUiMutation((state) => {
|
||||
// 取消选中:只清掉「轨迹点」这一层,不动角色本身的选择,避免点一下空白就把对象也取消选中
|
||||
if (!objectId || !pointId) {
|
||||
return state.selectedTrajectoryPoint ? { ...state, selectedTrajectoryPoint: null } : state;
|
||||
}
|
||||
|
||||
const target = state.project.objects.find((item) => item.id === objectId);
|
||||
if (!target?.trajectoryPoints?.some((point) => point.id === pointId)) return state;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user