|
1 | 1 | <script lang="ts"> |
2 | | - import { getContext, onMount, tick } from "svelte"; |
| 2 | + import { getContext, onMount, onDestroy, tick } from "svelte"; |
3 | 3 |
|
4 | 4 | import type { Editor } from "@graphite/editor"; |
5 | 5 | import { |
|
20 | 20 | import type { DocumentState } from "@graphite/state-providers/document"; |
21 | 21 | import { textInputCleanup } from "@graphite/utility-functions/keyboard-entry"; |
22 | 22 | import { extractPixelData, rasterizeSVGCanvas } from "@graphite/utility-functions/rasterization"; |
23 | | - import { updateBoundsOfViewports as updateViewport } from "@graphite/utility-functions/viewports"; |
| 23 | + import { setupViewportResizeObserver, cleanupViewportResizeObserver } from "@graphite/utility-functions/viewports"; |
24 | 24 |
|
25 | 25 | import EyedropperPreview, { ZOOM_WINDOW_DIMENSIONS } from "@graphite/components/floating-menus/EyedropperPreview.svelte"; |
26 | 26 | import LayoutCol from "@graphite/components/layout/LayoutCol.svelte"; |
|
209 | 209 | const logicalWidth = parseInt(foreignObject.getAttribute("width") || "0"); |
210 | 210 | const logicalHeight = parseInt(foreignObject.getAttribute("height") || "0"); |
211 | 211 |
|
212 | | - // if (canvasName !== "0" && canvas.parentElement) { |
213 | | - // console.log("test"); |
214 | | - // var newCanvas = window.document.createElement("canvas"); |
215 | | - // var context = newCanvas.getContext("2d"); |
| 212 | + // Clone canvas for repeated instances (layers that appear multiple times) |
| 213 | + // Viewport canvas is marked with data-is-viewport and should never be cloned |
| 214 | + const isViewport = placeholder.hasAttribute("data-is-viewport"); |
| 215 | + if (!isViewport && canvas.parentElement) { |
| 216 | + const newCanvas = window.document.createElement("canvas"); |
| 217 | + const context = newCanvas.getContext("2d"); |
216 | 218 |
|
217 | | - // newCanvas.width = canvas.width; |
218 | | - // newCanvas.height = canvas.height; |
| 219 | + newCanvas.width = canvas.width; |
| 220 | + newCanvas.height = canvas.height; |
219 | 221 |
|
220 | | - // context?.drawImage(canvas, 0, 0); |
| 222 | + context?.drawImage(canvas, 0, 0); |
221 | 223 |
|
222 | | - // canvas = newCanvas; |
223 | | - // } |
| 224 | + canvas = newCanvas; |
| 225 | + } |
224 | 226 |
|
225 | 227 | // Set CSS size to logical resolution (for correct display size) |
226 | 228 | canvas.style.width = `${logicalWidth}px`; |
|
404 | 406 | rulerHorizontal?.resize(); |
405 | 407 | rulerVertical?.resize(); |
406 | 408 |
|
407 | | - // Send the new bounds of the viewports to the backend |
408 | | - if (viewport.parentElement) updateViewport(editor); |
| 409 | + // Note: Viewport bounds are now sent to the backend by the ResizeObserver in viewports.ts |
| 410 | + // which provides pixel-perfect physical dimensions via devicePixelContentBoxSize |
409 | 411 | } |
410 | 412 |
|
411 | 413 | onMount(() => { |
|
484 | 486 | displayRemoveEditableTextbox(); |
485 | 487 | }); |
486 | 488 |
|
487 | | - // Once this component is mounted, we want to resend the document bounds to the backend via the resize event handler which does that |
488 | | - window.dispatchEvent(new Event("resize")); |
| 489 | + // Setup ResizeObserver for pixel-perfect viewport tracking with physical dimensions |
| 490 | + // This must happen in onMount to ensure the viewport container element exists |
| 491 | + setupViewportResizeObserver(editor); |
489 | 492 |
|
| 493 | + // Also observe the inner viewport for canvas sizing and ruler updates |
490 | 494 | const viewportResizeObserver = new ResizeObserver(() => { |
491 | 495 | updateViewportInfo(); |
492 | 496 | }); |
493 | 497 | if (viewport) viewportResizeObserver.observe(viewport); |
494 | 498 | }); |
| 499 | +
|
| 500 | + onDestroy(() => { |
| 501 | + // Cleanup the viewport resize observer |
| 502 | + cleanupViewportResizeObserver(); |
| 503 | + }); |
495 | 504 | </script> |
496 | 505 |
|
497 | 506 | <LayoutCol class="document" on:dragover={(e) => e.preventDefault()} on:drop={dropFile}> |
|
0 commit comments