fix: 优化空格播放和截图角色标签对不上

This commit is contained in:
2026-09-17 19:39:24 +08:00
parent 8d67f17a84
commit e66e3374bf
19 changed files with 633 additions and 58 deletions

View File

@@ -1,7 +1,26 @@
import { beforeEach, expect, it } from "vitest";
import { beforeEach, expect, it, vi } from "vitest";
import { Object3D, PerspectiveCamera, Scene, Vector2 } from "three";
import { usePlaybackStore } from "../runtime/playbackStore";
import { useDirectorStore } from "../store/directorStore";
import { exportVideo } from "./videoExport";
vi.mock("mp4-muxer", () => ({
Muxer: class {
target = { buffer: new ArrayBuffer(8) };
addVideoChunk() {}
finalize() {}
},
ArrayBufferTarget: class {},
}));
const contextRef = vi.hoisted(() => ({
current: null as null | { gl: any; scene: Scene; camera: PerspectiveCamera },
}));
vi.mock("./videoExportBridge", () => ({
getVideoExportContext: () => contextRef.current,
}));
beforeEach(() => {
usePlaybackStore.setState({ isPlaying: false, playhead: 0, duration: 5, loop: false, editingObjectId: null });
});
@@ -44,3 +63,68 @@ it("keeps the playhead where the export found it", async () => {
expect(usePlaybackStore.getState().playhead).toBe(2.5);
});
it("hides editor helpers (gizmo / grid / axes) while rendering exported frames, then restores them", async () => {
// 模拟场景里有一个「导出时隐藏」的辅助物体(也就是选中对象拖拽 gizmo 的那三条红绿蓝轴)
const scene = new Scene();
const helper = new Object3D();
helper.userData.hideFromViewportCapture = true;
const solidObject = new Object3D();
scene.add(helper, solidObject);
const visibilityDuringRender: boolean[] = [];
const gl = {
domElement: document.createElement("canvas"),
render: vi.fn(() => {
// 在渲染这一帧的瞬间记录辅助物体的可见性
visibilityDuringRender.push(helper.visible);
}),
getSize: (size: Vector2) => size.set(300, 150),
getPixelRatio: () => 1,
setPixelRatio: () => {},
setSize: () => {},
};
const camera = new PerspectiveCamera();
contextRef.current = { gl, scene, camera };
// 短时长,缩小导出帧数,加速测试
useDirectorStore.getState().setTimelineDuration(0.1);
// jsdom 没有 WebCodecs,这里把逐帧导出需要的几个全局桩好
const VideoEncoderStub = class {
static isConfigSupported = () => Promise.resolve({ supported: true });
output = () => {};
error = () => {};
encodeQueueSize = 0;
configure() {}
encode() {}
flush() {
return Promise.resolve();
}
close() {}
};
const VideoFrameStub = class {
close() {}
};
vi.stubGlobal("VideoEncoder", VideoEncoderStub);
vi.stubGlobal("VideoFrame", VideoFrameStub);
vi.stubGlobal("URL", {
...URL,
createObjectURL: () => "blob:fake",
revokeObjectURL: () => {},
});
try {
await exportVideo("director");
} finally {
vi.unstubAllGlobals();
}
expect(gl.render).toHaveBeenCalled();
// 每一帧渲染时,辅助物体都必须藏起来,否则导出的视频里会带着 gizmo 红绿蓝轴
expect(visibilityDuringRender.length).toBeGreaterThan(0);
expect(visibilityDuringRender.every((v) => v === false)).toBe(true);
// 导出结束后原样恢复
expect(helper.visible).toBe(true);
expect(solidObject.visible).toBe(true);
});