feat: 增加轨迹
This commit is contained in:
1 parent
b9fd289caf
commit
e74cf6d28b
42 files changed
+4010
-570
No files matched your search
@@ -4,6 +4,7 @@ import { afterEach, beforeEach, vi } from "vitest";
|
||||
import { clearViewportCaptureHandler, setViewportCaptureHandler } from "../io/captureBridge";
|
||||
import { BODY_TYPE_OPTIONS } from "../runtime/mannequin/bodyTypes";
|
||||
import { createInitialDirectorState, useDirectorStore } from "../store/directorStore";
|
||||
import { usePlaybackStore } from "../runtime/playbackStore";
|
||||
import { getCameraRigPositionFromViewSnapshot, getCameraViewSnapshotFromShot } from "../schema/cameraGeometry";
|
||||
import { ViewportToolbar } from "./ViewportToolbar";
|
||||
import { getModelLibraryItems } from "../modelLibrary/modelLibraryCatalog";
|
||||
@@ -71,6 +72,8 @@ it("renders the viewport capsule as project icon-system buttons", () => {
|
||||
"四方位截图",
|
||||
"十二方位截图",
|
||||
"全屏",
|
||||
"添加轨迹",
|
||||
"录制",
|
||||
];
|
||||
|
||||
expectedActions.forEach((label) => {
|
||||
@@ -86,6 +89,8 @@ it("renders the viewport capsule as project icon-system buttons", () => {
|
||||
button.getAttribute("aria-label")
|
||||
);
|
||||
expect(toolbarButtonLabels.indexOf("模型库")).toBe(toolbarButtonLabels.indexOf("导入本地模型") + 1);
|
||||
// 打点 / 录制固定在工具条最右侧
|
||||
expect(toolbarButtonLabels.slice(-2)).toEqual(["添加轨迹", "录制"]);
|
||||
});
|
||||
|
||||
it("renders custom hover labels instead of native title tooltips", () => {
|
||||
@@ -229,6 +234,80 @@ it("stores viewport capsule screenshots in the current camera while already in c
|
||||
expect(activeCamera?.lastCaptureUrl).toBe("data:image/png;base64,camera-view-4");
|
||||
});
|
||||
|
||||
it("disables the add-trajectory action until a character is selected", () => {
|
||||
render(<ViewportToolbar />);
|
||||
|
||||
const button = screen.getByRole("button", { name: "添加轨迹" });
|
||||
|
||||
expect(button).toBeDisabled();
|
||||
expect(button).toHaveClass("viewport-toolbar-button");
|
||||
expect(button.querySelector("svg")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("keeps the add-trajectory action disabled for non-character objects", () => {
|
||||
useDirectorStore.getState().selectObject("cam_object_1");
|
||||
|
||||
render(<ViewportToolbar />);
|
||||
|
||||
expect(screen.getByRole("button", { name: "添加轨迹" })).toBeDisabled();
|
||||
});
|
||||
|
||||
it("enters and leaves the canvas placement mode from the add-trajectory action", async () => {
|
||||
const user = userEvent.setup();
|
||||
useDirectorStore.getState().selectObject("char_default_a");
|
||||
|
||||
render(<ViewportToolbar />);
|
||||
|
||||
const button = screen.getByRole("button", { name: "添加轨迹" });
|
||||
expect(button).not.toBeDisabled();
|
||||
expect(button).toHaveAttribute("aria-pressed", "false");
|
||||
|
||||
await user.click(button);
|
||||
|
||||
expect(useDirectorStore.getState().trajectoryPlacementObjectId).toBe("char_default_a");
|
||||
expect(button).toHaveAttribute("aria-pressed", "true");
|
||||
|
||||
await user.click(button);
|
||||
|
||||
expect(useDirectorStore.getState().trajectoryPlacementObjectId).toBeNull();
|
||||
expect(button).toHaveAttribute("aria-pressed", "false");
|
||||
});
|
||||
|
||||
it("stops placing trajectory points when Escape is pressed", async () => {
|
||||
const user = userEvent.setup();
|
||||
useDirectorStore.getState().selectObject("char_default_a");
|
||||
|
||||
render(<ViewportToolbar />);
|
||||
|
||||
await user.click(screen.getByRole("button", { name: "添加轨迹" }));
|
||||
expect(useDirectorStore.getState().trajectoryPlacementObjectId).toBe("char_default_a");
|
||||
|
||||
await user.keyboard("{Escape}");
|
||||
expect(useDirectorStore.getState().trajectoryPlacementObjectId).toBeNull();
|
||||
});
|
||||
|
||||
it("hides and shows the floating timeline from the record action", async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
useDirectorStore.getState().setTimelineVisible(true);
|
||||
render(<ViewportToolbar />);
|
||||
|
||||
const recordButton = screen.getByRole("button", { name: "录制" });
|
||||
expect(recordButton).toHaveAttribute("aria-pressed", "true");
|
||||
expect(recordButton).toHaveClass("is-recording");
|
||||
|
||||
await user.click(recordButton);
|
||||
|
||||
expect(useDirectorStore.getState().timelineVisible).toBe(false);
|
||||
expect(recordButton).toHaveAttribute("aria-pressed", "false");
|
||||
expect(recordButton).not.toHaveClass("is-recording");
|
||||
|
||||
await user.click(screen.getByRole("button", { name: "录制" }));
|
||||
|
||||
expect(useDirectorStore.getState().timelineVisible).toBe(true);
|
||||
expect(screen.getByRole("button", { name: "录制" })).toHaveAttribute("aria-pressed", "true");
|
||||
});
|
||||
|
||||
it("switches the active transform control mode from the viewport capsule", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<ViewportToolbar />);
|
||||
|
||||
Reference in new issue
Block a user