feat(时间轴): 动作片段支持拖拽调整起止时间
This commit is contained in:
@@ -60,6 +60,9 @@ import {
|
||||
getTrajectoryPointTimes,
|
||||
getTrajectoryStartTime,
|
||||
shiftTrajectoryClip,
|
||||
getMotionStartTime,
|
||||
getMotionEndTime,
|
||||
MOTION_CLIP_MIN_DURATION,
|
||||
} from "../schema/trajectoryMotion";
|
||||
|
||||
export type TransformMode = "translate" | "rotate" | "scale";
|
||||
@@ -170,6 +173,12 @@ export interface DirectorActions {
|
||||
setTrajectoryClipTrimStart: (objectId: string, startTime: number) => void;
|
||||
/** 设置人物轨迹片段的结束时间(停留时长):角色走完最后一点后原地停留到这一刻,拖条右端把手时写入 */
|
||||
setTrajectoryClipEnd: (objectId: string, endTime: number) => void;
|
||||
/** 整体平移动作片段(拖动作条本体):片段起点与结束时间同步平移,保持动作时长不变 */
|
||||
setMotionClipStart: (objectId: string, startTime: number) => void;
|
||||
/** 修剪动作片段起点(拖动作条左端把手):只挪动作开始时间,结束时间不动 */
|
||||
setMotionClipTrimStart: (objectId: string, startTime: number) => void;
|
||||
/** 设置动作片段的结束时间(拖动作条右端把手):动作播放到这一刻停止,起点不动 */
|
||||
setMotionClipEnd: (objectId: string, endTime: number) => void;
|
||||
removeTrajectoryPoint: (objectId: string, pointId: string) => void;
|
||||
/** 选中某个轨迹点;传 null 表示取消选中(点到轨道空白处 / 面板其它地方时清掉轨迹关键帧的选中态) */
|
||||
selectTrajectoryPoint: (objectId: string | null, pointId: string | null) => void;
|
||||
@@ -1903,6 +1912,62 @@ export const useDirectorStore = create<DirectorStore>((set, get) => {
|
||||
}),
|
||||
},
|
||||
})),
|
||||
setMotionClipStart: (objectId, startTime) =>
|
||||
commitMutation((state) => ({
|
||||
...state,
|
||||
project: {
|
||||
...state.project,
|
||||
objects: updateObjectById(state.project.objects, objectId, (item) => {
|
||||
const duration = state.project.timeline.duration;
|
||||
const currentStart = getMotionStartTime(item, duration);
|
||||
const currentEnd = getMotionEndTime(item, duration, currentStart);
|
||||
// 片段刚性平移:动作时长(起点→终点)保持不变,起点夹在时间轴内,终点跟着走同样的量。
|
||||
const minSpan = MOTION_CLIP_MIN_DURATION;
|
||||
const span = Math.max(currentEnd - currentStart, minSpan);
|
||||
const upper = Math.max(duration - span, 0);
|
||||
const nextStart = Math.min(Math.max(startTime, 0), upper);
|
||||
const delta = nextStart - currentStart;
|
||||
|
||||
return {
|
||||
...item,
|
||||
motionStartTime: nextStart,
|
||||
motionEndTime: currentEnd + delta,
|
||||
};
|
||||
}),
|
||||
},
|
||||
})),
|
||||
setMotionClipTrimStart: (objectId, startTime) =>
|
||||
commitMutation((state) => ({
|
||||
...state,
|
||||
project: {
|
||||
...state.project,
|
||||
objects: updateObjectById(state.project.objects, objectId, (item) => {
|
||||
const duration = state.project.timeline.duration;
|
||||
const currentEnd = getMotionEndTime(item, duration, getMotionStartTime(item, duration));
|
||||
// 起点最多挪到「结束时间再往前一个最短时长」处,不能越过终点,否则片段就空了。
|
||||
const upper = Math.max(currentEnd - MOTION_CLIP_MIN_DURATION, 0);
|
||||
const nextStart = Math.min(Math.max(startTime, 0), upper);
|
||||
|
||||
return { ...item, motionStartTime: nextStart };
|
||||
}),
|
||||
},
|
||||
})),
|
||||
setMotionClipEnd: (objectId, endTime) =>
|
||||
commitMutation((state) => ({
|
||||
...state,
|
||||
project: {
|
||||
...state.project,
|
||||
objects: updateObjectById(state.project.objects, objectId, (item) => {
|
||||
const duration = state.project.timeline.duration;
|
||||
const currentStart = getMotionStartTime(item, duration);
|
||||
// 结束时间夹在「起点 + 最短时长 ~ 时间轴末尾」之间,保证片段始终合法、不为空。
|
||||
const lower = currentStart + MOTION_CLIP_MIN_DURATION;
|
||||
const nextEnd = Math.min(Math.max(endTime, lower), duration);
|
||||
|
||||
return { ...item, motionEndTime: nextEnd };
|
||||
}),
|
||||
},
|
||||
})),
|
||||
removeTrajectoryPoint: (objectId, pointId) =>
|
||||
commitMutation((state) => ({
|
||||
...state,
|
||||
|
||||
Reference in New Issue
Block a user