diff --git a/apps/ai-game-creator-shell/tests/previewWorkspaceZoom.test.tsx b/apps/ai-game-creator-shell/tests/previewWorkspaceZoom.test.tsx
index 908086f1d..50a89c1e2 100644
--- a/apps/ai-game-creator-shell/tests/previewWorkspaceZoom.test.tsx
+++ b/apps/ai-game-creator-shell/tests/previewWorkspaceZoom.test.tsx
@@ -87,13 +87,17 @@ function createCanvas(
};
}
-function renderedScale(container: HTMLElement) {
- const transform = (
- container.querySelector('.genarrative-image-canvas__world') as HTMLElement
- ).style.transform;
- const scale = /scale\(([^)]+)\)/.exec(transform)?.[1];
- if (!scale) throw new Error(`无法从 ${transform} 读取缩放值`);
- return Number(scale);
+function logicalViewportScale(container: HTMLElement) {
+ const world = container.querySelector(
+ '.genarrative-image-canvas__world',
+ ) as HTMLElement;
+ const inverseScale = Number(
+ world.style.getPropertyValue('--genarrative-image-canvas-inverse-scale'),
+ );
+ if (!Number.isFinite(inverseScale) || inverseScale <= 0) {
+ throw new Error(`无法从 world 读取逻辑缩放值`);
+ }
+ return 1 / inverseScale;
}
beforeEach(() => {
@@ -110,7 +114,7 @@ describe('PreviewWorkspace quick zoom', () => {
it('uses the toolbar zoom step while the preview is hovered', () => {
const rendered = render();
const preview = screen.getByRole('region', { name: 'UI 预览画布' });
- const before = renderedScale(rendered.container);
+ const before = logicalViewportScale(rendered.container);
fireEvent.pointerEnter(preview);
const wasNotCancelled = fireEvent.keyDown(window, {
@@ -121,14 +125,14 @@ describe('PreviewWorkspace quick zoom', () => {
});
expect(wasNotCancelled).toBe(false);
- expect(renderedScale(rendered.container)).toBeCloseTo(before * 1.16);
+ expect(logicalViewportScale(rendered.container)).toBeCloseTo(before * 1.16);
});
it('requires hover or focus and ignores interactive targets', () => {
const rendered = render();
const preview = screen.getByRole('region', { name: 'UI 预览画布' });
const zoomButton = screen.getByRole('button', { name: '放大画布' });
- const initial = renderedScale(rendered.container);
+ const initial = logicalViewportScale(rendered.container);
expect(
fireEvent.keyDown(window, {
@@ -137,7 +141,7 @@ describe('PreviewWorkspace quick zoom', () => {
cancelable: true,
}),
).toBe(true);
- expect(renderedScale(rendered.container)).toBe(initial);
+ expect(logicalViewportScale(rendered.container)).toBe(initial);
fireEvent.pointerEnter(preview);
expect(
@@ -147,7 +151,7 @@ describe('PreviewWorkspace quick zoom', () => {
cancelable: true,
}),
).toBe(true);
- expect(renderedScale(rendered.container)).toBe(initial);
+ expect(logicalViewportScale(rendered.container)).toBe(initial);
fireEvent.pointerLeave(preview);
fireEvent.focus(preview);
@@ -157,13 +161,15 @@ describe('PreviewWorkspace quick zoom', () => {
ctrlKey: true,
cancelable: true,
});
- expect(renderedScale(rendered.container)).toBeCloseTo(initial * 0.86);
+ expect(logicalViewportScale(rendered.container)).toBeCloseTo(
+ initial * 0.86,
+ );
});
it('keeps actual-size and fit shortcuts within the preview scope', () => {
const rendered = render();
const preview = screen.getByRole('region', { name: 'UI 预览画布' });
- const fitted = renderedScale(rendered.container);
+ const fitted = logicalViewportScale(rendered.container);
fireEvent.focus(preview);
fireEvent.keyDown(window, {
@@ -171,14 +177,14 @@ describe('PreviewWorkspace quick zoom', () => {
ctrlKey: true,
cancelable: true,
});
- expect(renderedScale(rendered.container)).toBe(1);
+ expect(logicalViewportScale(rendered.container)).toBe(1);
fireEvent.keyDown(window, {
key: '0',
ctrlKey: true,
cancelable: true,
});
- expect(renderedScale(rendered.container)).toBe(fitted);
+ expect(logicalViewportScale(rendered.container)).toBe(fitted);
});
it('leaves browser zoom untouched when the preview has no content', () => {
diff --git a/docs/【交互设计】预览画布缩放滑杆-2026-09-05.md b/docs/【交互设计】预览画布缩放滑杆-2026-09-05.md
index 198d7788a..497fb7e85 100644
--- a/docs/【交互设计】预览画布缩放滑杆-2026-09-05.md
+++ b/docs/【交互设计】预览画布缩放滑杆-2026-09-05.md
@@ -19,6 +19,7 @@
- 事件来自输入框、文本编辑区、按钮、链接、缩放工具栏或其他可操作控件时不拦截;不向 iframe 子文档注入监听。
- 仅在存在可缩放视口且确认命中快捷键时调用 `preventDefault()` 与 `stopPropagation()`,防止浏览器页面同时缩放。
- 保留现有 `Ctrl/Cmd+0` 适配画布与 `Ctrl/Cmd+1` 恢复 100% 行为,不增加其他重置快捷键。缩放仍是当前预览实例的临时 UI 状态。
+- 快捷键和百分比使用逻辑 `viewport.scale` 作为缩放真相;共享 `CanvasWorld` 的渲染 transform 可能包含超采样换算,验证时不得直接把 CSS transform 值当作用户可见比例。
- 保留快捷键与缩放焦点边界的组件级回归测试,不扩展端到端测试。
## 验收