From e9ca9a456bf7f622012733a633b5b88f27281052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Tue, 8 Sep 2026 17:43:09 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E9=A2=84=E8=A7=88=E7=94=BB?= =?UTF-8?q?=E5=B8=83=E5=BF=AB=E6=8D=B7=E7=BC=A9=E6=94=BE=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 预览缩放测试改按逻辑 viewport.scale 断言 补充超采样渲染与用户缩放比例的验证约定 --- .../tests/previewWorkspaceZoom.test.tsx | 38 +++++++++++-------- ...【交互设计】预览画布缩放滑杆-2026-09-05.md | 1 + 2 files changed, 23 insertions(+), 16 deletions(-) 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 值当作用户可见比例。 - 保留快捷键与缩放焦点边界的组件级回归测试,不扩展端到端测试。 ## 验收