feat: 增加轨迹

This commit is contained in:
2026-09-17 09:09:06 +08:00
parent b9fd289caf
commit e74cf6d28b
42 changed files with 4010 additions and 570 deletions

View File

@@ -219,6 +219,40 @@ it("lists visible viewport models as camera focus targets and centers on the sel
expect(screen.getByLabelText("注视坐标 Z")).toHaveValue(0);
});
it("resets the camera position and the look-at target from their own title buttons", async () => {
const user = userEvent.setup();
render(<CameraPanel />);
const readCamera = () => useDirectorStore.getState().project.cameras.find((item) => item.id === "cam_1");
const seededCamera = readCamera();
expect(seededCamera).toBeTruthy();
["位置", "注视坐标"].forEach((label) => {
const button = within(screen.getByRole("group", { name: label })).getByRole("button", { name: `重置${label}` });
expect(button).toHaveClass("inspector-axis-reset-button");
expect(button.querySelector("svg")).toBeInTheDocument();
});
useDirectorStore.getState().updateCamera("cam_1", {
transform: { ...seededCamera!.transform, position: [7, 3, 7] },
target: [1, 2, 3],
});
await user.click(screen.getByRole("button", { name: "重置位置" }));
// 机位回到默认机位位置(不是归零:相机压在世界原点会让视线方向退化)
expect(readCamera()?.transform.position).toEqual([0, 1.55, 5.4]);
// 只重置位置,注视点保持原样
expect(readCamera()?.target).toEqual([1, 2, 3]);
await user.click(screen.getByRole("button", { name: "重置注视坐标" }));
expect(readCamera()?.target).toEqual([0, 1.05, 0]);
expect(readCamera()?.targetMode).toBe("manual");
expect(screen.getByLabelText("注视坐标 Z")).toHaveValue(0);
});
it("updates camera position and target coordinates across all axes", async () => {
const user = userEvent.setup();
render(<CameraPanel />);
@@ -418,7 +452,7 @@ it("clears every camera screenshot from the screenshots tab and shows the empty
]);
});
it("closes the capture viewer when clicking outside the image", async () => {
it("renders the capture viewer as a portal outside the side panel", async () => {
const user = userEvent.setup();
seedCameraCapture();
@@ -426,8 +460,39 @@ it("closes the capture viewer when clicking outside the image", async () => {
await user.click(screen.getByLabelText("查看截图 机位01-截图01"));
const viewer = screen.getByRole("dialog", { name: "相机截图查看器" });
// 侧栏卡片带 backdrop-filter,会成为 fixed 定位元素的包含块:查看器必须挂在 body 上,
// 否则 position: fixed; inset: 0 会相对侧栏解析并被裁剪,看起来就不是弹出层了
expect(viewer.parentElement).toBe(document.body);
expect(container.contains(viewer)).toBe(false);
expect(viewer).toHaveAttribute("aria-modal", "true");
});
it("opens the capture viewer when the capture card itself is clicked", async () => {
const user = userEvent.setup();
seedCameraCapture();
render(<CameraPanel />);
// 点名称(缩略图外的区域)也应该弹出大图
await user.click(screen.getByText("机位01-截图01"));
expect(screen.getByRole("dialog", { name: "相机截图查看器" })).toBeInTheDocument();
expect(screen.getByAltText("机位01-截图01 查看大图")).toBeInTheDocument();
});
it("closes the capture viewer when clicking outside the image", async () => {
const user = userEvent.setup();
seedCameraCapture();
render(<CameraPanel />);
await user.click(screen.getByLabelText("查看截图 机位01-截图01"));
const previewImage = screen.getByAltText("机位01-截图01 查看大图");
const viewerStage = container.querySelector(".camera-capture-viewer-stage");
// 查看器通过 portal 挂在 body 上,所以要直接从 document 查
const viewerStage = document.querySelector(".camera-capture-viewer-stage");
expect(viewerStage).toBeInTheDocument();