From 08cda72ea000a2be311e63e42625aa528af587fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Fri, 24 Jul 2026 16:08:49 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=8D=E7=94=A8=E7=94=BB=E5=B8=83=E5=B0=BA?= =?UTF-8?q?=E5=AF=B8=E7=8A=B6=E6=80=81=E7=94=9F=E6=88=90=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E5=B0=81=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 由 ResizeObserver 维护的 canvasSize 传入项目持久化 hook 移除持久化层对 canvasViewportRef 和 clientWidth/clientHeight 的直接读取 更新持久化测试夹具并保持封面尺寸断言 --- .../image-editor/ImageCanvasEditorView.tsx | 2 +- .../useImageCanvasProjectPersistence.test.tsx | 4 +-- .../useImageCanvasProjectPersistence.ts | 26 +++++-------------- 3 files changed, 9 insertions(+), 23 deletions(-) diff --git a/src/components/image-editor/ImageCanvasEditorView.tsx b/src/components/image-editor/ImageCanvasEditorView.tsx index dc4512b39..1e50147d2 100644 --- a/src/components/image-editor/ImageCanvasEditorView.tsx +++ b/src/components/image-editor/ImageCanvasEditorView.tsx @@ -1116,7 +1116,6 @@ export function ImageCanvasEditorView({ () => ({ layersRef, viewportRef, - canvasViewportRef, canvasGenerationDialogsRef, canvasBackgroundColorRef, }), @@ -1156,6 +1155,7 @@ export function ImageCanvasEditorView({ layers, canvasGenerationDialogs, viewport, + canvasSize, canvasBackgroundColor, isViewportInteracting, canAccessProtectedData: authUi ? authUi.canAccessProtectedData : true, diff --git a/src/components/image-editor/useImageCanvasProjectPersistence.test.tsx b/src/components/image-editor/useImageCanvasProjectPersistence.test.tsx index 8ebb77d8f..1673a2011 100644 --- a/src/components/image-editor/useImageCanvasProjectPersistence.test.tsx +++ b/src/components/image-editor/useImageCanvasProjectPersistence.test.tsx @@ -113,12 +113,12 @@ function ProjectPersistenceHarness({ y: 0, scale: 1, }); + const canvasSize = { width: 900, height: 640 }; const [isViewportInteracting, setIsViewportInteracting] = useState(false); const [projectTitle, setProjectTitle] = useState(''); const [projectRenameValue, setProjectRenameValue] = useState(''); const layersRef = useRef(layers); const viewportRef = useRef(viewport); - const canvasViewportRef = useRef(null); const canvasGenerationDialogsRef = useRef(generationDialogs); const canvasBackgroundColorRef = useRef(canvasBackgroundColor); const selectedLayerRef = useRef(null); @@ -147,7 +147,6 @@ function ProjectPersistenceHarness({ () => ({ layersRef, viewportRef, - canvasViewportRef, canvasGenerationDialogsRef, canvasBackgroundColorRef, }), @@ -173,6 +172,7 @@ function ProjectPersistenceHarness({ layers, canvasGenerationDialogs: generationDialogs, viewport, + canvasSize, canvasBackgroundColor, isViewportInteracting, canAccessProtectedData, diff --git a/src/components/image-editor/useImageCanvasProjectPersistence.ts b/src/components/image-editor/useImageCanvasProjectPersistence.ts index b1617927e..44a447ce4 100644 --- a/src/components/image-editor/useImageCanvasProjectPersistence.ts +++ b/src/components/image-editor/useImageCanvasProjectPersistence.ts @@ -22,7 +22,6 @@ import { canvasDisplayViewportToViewport, type CanvasLayerResourceMetadata, DEFAULT_CANVAS_BACKGROUND_COLOR, - DEFAULT_CANVAS_SIZE, hydrateLayer, isInlineEditorMediaSource, serializeCanvasLayout, @@ -40,6 +39,7 @@ import { PROJECT_COVER_SNAPSHOT_ASSET_KIND, PROJECT_COVER_SNAPSHOT_OBJECT_ASSET_KIND, PROJECT_COVER_SNAPSHOT_SIZE, + type ProjectCoverSnapshotViewportSize, } from './ImageCanvasProjectCoverSnapshotModel'; type ProjectResourceOptions = { @@ -66,7 +66,6 @@ type CachedEditorProjectSnapshot = { type ImageCanvasProjectPersistenceRefs = { layersRef: RefObject; viewportRef: RefObject; - canvasViewportRef: RefObject; canvasGenerationDialogsRef: RefObject; canvasBackgroundColorRef: RefObject; }; @@ -90,6 +89,7 @@ type ImageCanvasProjectPersistenceOptions = { layers: CanvasLayer[]; canvasGenerationDialogs: CanvasGenerationDialogState[]; viewport: CanvasViewport; + canvasSize: ProjectCoverSnapshotViewportSize; canvasBackgroundColor: string; isViewportInteracting: boolean; canAccessProtectedData: boolean; @@ -116,21 +116,6 @@ function isEditorProjectRevisionConflict(error: unknown) { return error instanceof ApiClientError && error.status === 409; } -function resolveProjectCoverViewportSize( - viewportElement: HTMLDivElement | null, -) { - const width = viewportElement?.clientWidth ?? 0; - const height = viewportElement?.clientHeight ?? 0; - return { - width: - width > 0 && Number.isFinite(width) ? width : DEFAULT_CANVAS_SIZE.width, - height: - height > 0 && Number.isFinite(height) - ? height - : DEFAULT_CANVAS_SIZE.height, - }; -} - const EDITOR_PROJECT_LAYOUT_RETRYABLE_STATUS_CODES = new Set([ 408, 425, 429, 502, 503, 504, ]); @@ -330,6 +315,7 @@ export function useImageCanvasProjectPersistence({ layers, canvasGenerationDialogs, viewport, + canvasSize, canvasBackgroundColor, isViewportInteracting, canAccessProtectedData, @@ -354,6 +340,7 @@ export function useImageCanvasProjectPersistence({ const projectTitleRef = useRef('未命名画布'); const coverSnapshotSignatureRef = useRef(null); const coverSnapshotUploadRequestRef = useRef(0); + const coverSnapshotViewportSizeRef = useRef(canvasSize); const [projectId, setProjectId] = useState(null); const [isProjectReady, setIsProjectReady] = useState(false); const { @@ -366,6 +353,7 @@ export function useImageCanvasProjectPersistence({ restoreCanvasGenerationDialogs, applyCanvasBackgroundColor, } = setters; + coverSnapshotViewportSizeRef.current = canvasSize; const clearPendingProjectLayoutSave = useCallback(() => { pendingProjectLayoutSaveRef.current = null; @@ -478,9 +466,7 @@ export function useImageCanvasProjectPersistence({ } const coverViewport = canvasDisplayViewportToViewport(coverDisplayViewport); - const viewportSize = resolveProjectCoverViewportSize( - refs.canvasViewportRef.current, - ); + const viewportSize = coverSnapshotViewportSizeRef.current; const backgroundColor = refs.canvasBackgroundColorRef.current; const signature = buildProjectCoverSnapshotSignature({ layers: coverLayers,