mirror of
				https://github.com/excalidraw/excalidraw.git
				synced 2025-10-30 18:34:22 +01:00 
			
		
		
		
	fix: emitted visible scene bounds not accounting for offsets (#7450)
This commit is contained in:
		| @@ -20,9 +20,12 @@ export const WS_EVENTS = { | |||||||
| } as const; | } as const; | ||||||
|  |  | ||||||
| export enum WS_SUBTYPES { | export enum WS_SUBTYPES { | ||||||
|  |   INVALID_RESPONSE = "INVALID_RESPONSE", | ||||||
|   INIT = "SCENE_INIT", |   INIT = "SCENE_INIT", | ||||||
|   UPDATE = "SCENE_UPDATE", |   UPDATE = "SCENE_UPDATE", | ||||||
|   USER_VIEWPORT_BOUNDS = "USER_VIEWPORT_BOUNDS", |   MOUSE_LOCATION = "MOUSE_LOCATION", | ||||||
|  |   IDLE_STATUS = "IDLE_STATUS", | ||||||
|  |   USER_VISIBLE_SCENE_BOUNDS = "USER_VISIBLE_SCENE_BOUNDS", | ||||||
| } | } | ||||||
|  |  | ||||||
| export const FIREBASE_STORAGE_PREFIXES = { | export const FIREBASE_STORAGE_PREFIXES = { | ||||||
|   | |||||||
| @@ -18,10 +18,10 @@ import { | |||||||
| } from "../../packages/excalidraw/index"; | } from "../../packages/excalidraw/index"; | ||||||
| import { Collaborator, Gesture } from "../../packages/excalidraw/types"; | import { Collaborator, Gesture } from "../../packages/excalidraw/types"; | ||||||
| import { | import { | ||||||
|  |   assertNever, | ||||||
|   preventUnload, |   preventUnload, | ||||||
|   resolvablePromise, |   resolvablePromise, | ||||||
|   throttleRAF, |   throttleRAF, | ||||||
|   viewportCoordsToSceneCoords, |  | ||||||
|   withBatchedUpdates, |   withBatchedUpdates, | ||||||
| } from "../../packages/excalidraw/utils"; | } from "../../packages/excalidraw/utils"; | ||||||
| import { | import { | ||||||
| @@ -81,7 +81,8 @@ import { resetBrowserStateVersions } from "../data/tabSync"; | |||||||
| import { LocalData } from "../data/LocalData"; | import { LocalData } from "../data/LocalData"; | ||||||
| import { atom, useAtom } from "jotai"; | import { atom, useAtom } from "jotai"; | ||||||
| import { appJotaiStore } from "../app-jotai"; | import { appJotaiStore } from "../app-jotai"; | ||||||
| import { Mutable } from "../../packages/excalidraw/utility-types"; | import { Mutable, ValueOf } from "../../packages/excalidraw/utility-types"; | ||||||
|  | import { getVisibleSceneBounds } from "../../packages/excalidraw/element/bounds"; | ||||||
|  |  | ||||||
| export const collabAPIAtom = atom<CollabAPI | null>(null); | export const collabAPIAtom = atom<CollabAPI | null>(null); | ||||||
| export const collabDialogShownAtom = atom(false); | export const collabDialogShownAtom = atom(false); | ||||||
| @@ -174,7 +175,7 @@ class Collab extends PureComponent<Props, CollabState> { | |||||||
|       this.portal.socket && this.portal.broadcastUserFollowed(payload); |       this.portal.socket && this.portal.broadcastUserFollowed(payload); | ||||||
|     }); |     }); | ||||||
|     const throttledRelayUserViewportBounds = throttleRAF( |     const throttledRelayUserViewportBounds = throttleRAF( | ||||||
|       this.relayUserViewportBounds, |       this.relayVisibleSceneBounds, | ||||||
|     ); |     ); | ||||||
|     const unsubOnScrollChange = this.excalidrawAPI.onScrollChange(() => |     const unsubOnScrollChange = this.excalidrawAPI.onScrollChange(() => | ||||||
|       throttledRelayUserViewportBounds(), |       throttledRelayUserViewportBounds(), | ||||||
| @@ -384,7 +385,7 @@ class Collab extends PureComponent<Props, CollabState> { | |||||||
|     iv: Uint8Array, |     iv: Uint8Array, | ||||||
|     encryptedData: ArrayBuffer, |     encryptedData: ArrayBuffer, | ||||||
|     decryptionKey: string, |     decryptionKey: string, | ||||||
|   ) => { |   ): Promise<ValueOf<SocketUpdateDataSource>> => { | ||||||
|     try { |     try { | ||||||
|       const decrypted = await decryptData(iv, encryptedData, decryptionKey); |       const decrypted = await decryptData(iv, encryptedData, decryptionKey); | ||||||
|  |  | ||||||
| @@ -396,7 +397,7 @@ class Collab extends PureComponent<Props, CollabState> { | |||||||
|       window.alert(t("alerts.decryptFailed")); |       window.alert(t("alerts.decryptFailed")); | ||||||
|       console.error(error); |       console.error(error); | ||||||
|       return { |       return { | ||||||
|         type: "INVALID_RESPONSE", |         type: WS_SUBTYPES.INVALID_RESPONSE, | ||||||
|       }; |       }; | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| @@ -512,7 +513,7 @@ class Collab extends PureComponent<Props, CollabState> { | |||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         switch (decryptedData.type) { |         switch (decryptedData.type) { | ||||||
|           case "INVALID_RESPONSE": |           case WS_SUBTYPES.INVALID_RESPONSE: | ||||||
|             return; |             return; | ||||||
|           case WS_SUBTYPES.INIT: { |           case WS_SUBTYPES.INIT: { | ||||||
|             if (!this.portal.socketInitialized) { |             if (!this.portal.socketInitialized) { | ||||||
| @@ -535,7 +536,7 @@ class Collab extends PureComponent<Props, CollabState> { | |||||||
|               this.reconcileElements(decryptedData.payload.elements), |               this.reconcileElements(decryptedData.payload.elements), | ||||||
|             ); |             ); | ||||||
|             break; |             break; | ||||||
|           case "MOUSE_LOCATION": { |           case WS_SUBTYPES.MOUSE_LOCATION: { | ||||||
|             const { pointer, button, username, selectedElementIds } = |             const { pointer, button, username, selectedElementIds } = | ||||||
|               decryptedData.payload; |               decryptedData.payload; | ||||||
|  |  | ||||||
| @@ -554,8 +555,8 @@ class Collab extends PureComponent<Props, CollabState> { | |||||||
|             break; |             break; | ||||||
|           } |           } | ||||||
|  |  | ||||||
|           case WS_SUBTYPES.USER_VIEWPORT_BOUNDS: { |           case WS_SUBTYPES.USER_VISIBLE_SCENE_BOUNDS: { | ||||||
|             const { bounds, socketId } = decryptedData.payload; |             const { sceneBounds, socketId } = decryptedData.payload; | ||||||
|  |  | ||||||
|             const appState = this.excalidrawAPI.getAppState(); |             const appState = this.excalidrawAPI.getAppState(); | ||||||
|  |  | ||||||
| @@ -579,7 +580,7 @@ class Collab extends PureComponent<Props, CollabState> { | |||||||
|             this.excalidrawAPI.updateScene({ |             this.excalidrawAPI.updateScene({ | ||||||
|               appState: zoomToFitBounds({ |               appState: zoomToFitBounds({ | ||||||
|                 appState, |                 appState, | ||||||
|                 bounds, |                 bounds: sceneBounds, | ||||||
|                 fitToViewport: true, |                 fitToViewport: true, | ||||||
|                 viewportZoomFactor: 1, |                 viewportZoomFactor: 1, | ||||||
|               }).appState, |               }).appState, | ||||||
| @@ -588,7 +589,7 @@ class Collab extends PureComponent<Props, CollabState> { | |||||||
|             break; |             break; | ||||||
|           } |           } | ||||||
|  |  | ||||||
|           case "IDLE_STATUS": { |           case WS_SUBTYPES.IDLE_STATUS: { | ||||||
|             const { userState, socketId, username } = decryptedData.payload; |             const { userState, socketId, username } = decryptedData.payload; | ||||||
|             this.updateCollaborator(socketId, { |             this.updateCollaborator(socketId, { | ||||||
|               userState, |               userState, | ||||||
| @@ -596,6 +597,10 @@ class Collab extends PureComponent<Props, CollabState> { | |||||||
|             }); |             }); | ||||||
|             break; |             break; | ||||||
|           } |           } | ||||||
|  |  | ||||||
|  |           default: { | ||||||
|  |             assertNever(decryptedData, null); | ||||||
|  |           } | ||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|     ); |     ); | ||||||
| @@ -618,7 +623,7 @@ class Collab extends PureComponent<Props, CollabState> { | |||||||
|           appState: { followedBy: new Set(followedBy) }, |           appState: { followedBy: new Set(followedBy) }, | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         this.relayUserViewportBounds({ shouldPerform: true }); |         this.relayVisibleSceneBounds({ force: true }); | ||||||
|       }, |       }, | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
| @@ -848,25 +853,14 @@ class Collab extends PureComponent<Props, CollabState> { | |||||||
|     CURSOR_SYNC_TIMEOUT, |     CURSOR_SYNC_TIMEOUT, | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|   relayUserViewportBounds = (props?: { shouldPerform: boolean }) => { |   relayVisibleSceneBounds = (props?: { force: boolean }) => { | ||||||
|     const appState = this.excalidrawAPI.getAppState(); |     const appState = this.excalidrawAPI.getAppState(); | ||||||
|  |  | ||||||
|     if ( |     if (this.portal.socket && (appState.followedBy.size > 0 || props?.force)) { | ||||||
|       this.portal.socket && |       this.portal.broadcastVisibleSceneBounds( | ||||||
|       (appState.followedBy.size > 0 || props?.shouldPerform) |         { | ||||||
|     ) { |           sceneBounds: getVisibleSceneBounds(appState), | ||||||
|       const { x: x1, y: y1 } = viewportCoordsToSceneCoords( |         }, | ||||||
|         { clientX: 0, clientY: 0 }, |  | ||||||
|         appState, |  | ||||||
|       ); |  | ||||||
|  |  | ||||||
|       const { x: x2, y: y2 } = viewportCoordsToSceneCoords( |  | ||||||
|         { clientX: appState.width, clientY: appState.height }, |  | ||||||
|         appState, |  | ||||||
|       ); |  | ||||||
|  |  | ||||||
|       this.portal.broadcastUserViewportBounds( |  | ||||||
|         { bounds: [x1, y1, x2, y2] }, |  | ||||||
|         `follow@${this.portal.socket.id}`, |         `follow@${this.portal.socket.id}`, | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -184,7 +184,7 @@ class Portal { | |||||||
|   broadcastIdleChange = (userState: UserIdleState) => { |   broadcastIdleChange = (userState: UserIdleState) => { | ||||||
|     if (this.socket?.id) { |     if (this.socket?.id) { | ||||||
|       const data: SocketUpdateDataSource["IDLE_STATUS"] = { |       const data: SocketUpdateDataSource["IDLE_STATUS"] = { | ||||||
|         type: "IDLE_STATUS", |         type: WS_SUBTYPES.IDLE_STATUS, | ||||||
|         payload: { |         payload: { | ||||||
|           socketId: this.socket.id, |           socketId: this.socket.id, | ||||||
|           userState, |           userState, | ||||||
| @@ -204,7 +204,7 @@ class Portal { | |||||||
|   }) => { |   }) => { | ||||||
|     if (this.socket?.id) { |     if (this.socket?.id) { | ||||||
|       const data: SocketUpdateDataSource["MOUSE_LOCATION"] = { |       const data: SocketUpdateDataSource["MOUSE_LOCATION"] = { | ||||||
|         type: "MOUSE_LOCATION", |         type: WS_SUBTYPES.MOUSE_LOCATION, | ||||||
|         payload: { |         payload: { | ||||||
|           socketId: this.socket.id, |           socketId: this.socket.id, | ||||||
|           pointer: payload.pointer, |           pointer: payload.pointer, | ||||||
| @@ -222,19 +222,19 @@ class Portal { | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   broadcastUserViewportBounds = ( |   broadcastVisibleSceneBounds = ( | ||||||
|     payload: { |     payload: { | ||||||
|       bounds: [number, number, number, number]; |       sceneBounds: SocketUpdateDataSource["USER_VISIBLE_SCENE_BOUNDS"]["payload"]["sceneBounds"]; | ||||||
|     }, |     }, | ||||||
|     roomId: string, |     roomId: string, | ||||||
|   ) => { |   ) => { | ||||||
|     if (this.socket?.id) { |     if (this.socket?.id) { | ||||||
|       const data: SocketUpdateDataSource["USER_VIEWPORT_BOUNDS"] = { |       const data: SocketUpdateDataSource["USER_VISIBLE_SCENE_BOUNDS"] = { | ||||||
|         type: WS_SUBTYPES.USER_VIEWPORT_BOUNDS, |         type: WS_SUBTYPES.USER_VISIBLE_SCENE_BOUNDS, | ||||||
|         payload: { |         payload: { | ||||||
|           socketId: this.socket.id, |           socketId: this.socket.id, | ||||||
|           username: this.collab.state.username, |           username: this.collab.state.username, | ||||||
|           bounds: payload.bounds, |           sceneBounds: payload.sceneBounds, | ||||||
|         }, |         }, | ||||||
|       }; |       }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ import { | |||||||
| import { serializeAsJSON } from "../../packages/excalidraw/data/json"; | import { serializeAsJSON } from "../../packages/excalidraw/data/json"; | ||||||
| import { restore } from "../../packages/excalidraw/data/restore"; | import { restore } from "../../packages/excalidraw/data/restore"; | ||||||
| import { ImportedDataState } from "../../packages/excalidraw/data/types"; | import { ImportedDataState } from "../../packages/excalidraw/data/types"; | ||||||
|  | import { SceneBounds } from "../../packages/excalidraw/element/bounds"; | ||||||
| import { isInvisiblySmallElement } from "../../packages/excalidraw/element/sizeHelpers"; | import { isInvisiblySmallElement } from "../../packages/excalidraw/element/sizeHelpers"; | ||||||
| import { isInitializedImageElement } from "../../packages/excalidraw/element/typeChecks"; | import { isInitializedImageElement } from "../../packages/excalidraw/element/typeChecks"; | ||||||
| import { | import { | ||||||
| @@ -28,6 +29,7 @@ import { | |||||||
|   DELETED_ELEMENT_TIMEOUT, |   DELETED_ELEMENT_TIMEOUT, | ||||||
|   FILE_UPLOAD_MAX_BYTES, |   FILE_UPLOAD_MAX_BYTES, | ||||||
|   ROOM_ID_BYTES, |   ROOM_ID_BYTES, | ||||||
|  |   WS_SUBTYPES, | ||||||
| } from "../app_constants"; | } from "../app_constants"; | ||||||
| import { encodeFilesForUpload } from "./FileManager"; | import { encodeFilesForUpload } from "./FileManager"; | ||||||
| import { saveFilesToFirebase } from "./firebase"; | import { saveFilesToFirebase } from "./firebase"; | ||||||
| @@ -97,20 +99,23 @@ export type EncryptedData = { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| export type SocketUpdateDataSource = { | export type SocketUpdateDataSource = { | ||||||
|  |   INVALID_RESPONSE: { | ||||||
|  |     type: WS_SUBTYPES.INVALID_RESPONSE; | ||||||
|  |   }; | ||||||
|   SCENE_INIT: { |   SCENE_INIT: { | ||||||
|     type: "SCENE_INIT"; |     type: WS_SUBTYPES.INIT; | ||||||
|     payload: { |     payload: { | ||||||
|       elements: readonly ExcalidrawElement[]; |       elements: readonly ExcalidrawElement[]; | ||||||
|     }; |     }; | ||||||
|   }; |   }; | ||||||
|   SCENE_UPDATE: { |   SCENE_UPDATE: { | ||||||
|     type: "SCENE_UPDATE"; |     type: WS_SUBTYPES.UPDATE; | ||||||
|     payload: { |     payload: { | ||||||
|       elements: readonly ExcalidrawElement[]; |       elements: readonly ExcalidrawElement[]; | ||||||
|     }; |     }; | ||||||
|   }; |   }; | ||||||
|   MOUSE_LOCATION: { |   MOUSE_LOCATION: { | ||||||
|     type: "MOUSE_LOCATION"; |     type: WS_SUBTYPES.MOUSE_LOCATION; | ||||||
|     payload: { |     payload: { | ||||||
|       socketId: string; |       socketId: string; | ||||||
|       pointer: { x: number; y: number; tool: "pointer" | "laser" }; |       pointer: { x: number; y: number; tool: "pointer" | "laser" }; | ||||||
| @@ -119,16 +124,16 @@ export type SocketUpdateDataSource = { | |||||||
|       username: string; |       username: string; | ||||||
|     }; |     }; | ||||||
|   }; |   }; | ||||||
|   USER_VIEWPORT_BOUNDS: { |   USER_VISIBLE_SCENE_BOUNDS: { | ||||||
|     type: "USER_VIEWPORT_BOUNDS"; |     type: WS_SUBTYPES.USER_VISIBLE_SCENE_BOUNDS; | ||||||
|     payload: { |     payload: { | ||||||
|       socketId: string; |       socketId: string; | ||||||
|       username: string; |       username: string; | ||||||
|       bounds: [number, number, number, number]; |       sceneBounds: SceneBounds; | ||||||
|     }; |     }; | ||||||
|   }; |   }; | ||||||
|   IDLE_STATUS: { |   IDLE_STATUS: { | ||||||
|     type: "IDLE_STATUS"; |     type: WS_SUBTYPES.IDLE_STATUS; | ||||||
|     payload: { |     payload: { | ||||||
|       socketId: string; |       socketId: string; | ||||||
|       userState: UserIdleState; |       userState: UserIdleState; | ||||||
| @@ -138,10 +143,7 @@ export type SocketUpdateDataSource = { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| export type SocketUpdateDataIncoming = | export type SocketUpdateDataIncoming = | ||||||
|   | SocketUpdateDataSource[keyof SocketUpdateDataSource] |   SocketUpdateDataSource[keyof SocketUpdateDataSource]; | ||||||
|   | { |  | ||||||
|       type: "INVALID_RESPONSE"; |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
| export type SocketUpdateData = | export type SocketUpdateData = | ||||||
|   SocketUpdateDataSource[keyof SocketUpdateDataSource] & { |   SocketUpdateDataSource[keyof SocketUpdateDataSource] & { | ||||||
|   | |||||||
| @@ -13,6 +13,8 @@ Please add the latest change on the top under the correct section. | |||||||
|  |  | ||||||
| ## Unreleased | ## Unreleased | ||||||
|  |  | ||||||
|  | - Expose `getVisibleSceneBounds` helper to get scene bounds of visible canvas area. [#7450](https://github.com/excalidraw/excalidraw/pull/7450) | ||||||
|  |  | ||||||
| ### Breaking Changes | ### Breaking Changes | ||||||
|  |  | ||||||
| - `appState.openDialog` type was changed from `null | string` to `null | { name: string }`. [#7336](https://github.com/excalidraw/excalidraw/pull/7336) | - `appState.openDialog` type was changed from `null | string` to `null | { name: string }`. [#7336](https://github.com/excalidraw/excalidraw/pull/7336) | ||||||
|   | |||||||
| @@ -20,7 +20,7 @@ import { | |||||||
|   isHandToolActive, |   isHandToolActive, | ||||||
| } from "../appState"; | } from "../appState"; | ||||||
| import { DEFAULT_CANVAS_BACKGROUND_PICKS } from "../colors"; | import { DEFAULT_CANVAS_BACKGROUND_PICKS } from "../colors"; | ||||||
| import { Bounds } from "../element/bounds"; | import { SceneBounds } from "../element/bounds"; | ||||||
| import { setCursor } from "../cursor"; | import { setCursor } from "../cursor"; | ||||||
|  |  | ||||||
| export const actionChangeViewBackgroundColor = register({ | export const actionChangeViewBackgroundColor = register({ | ||||||
| @@ -211,7 +211,7 @@ export const actionResetZoom = register({ | |||||||
| }); | }); | ||||||
|  |  | ||||||
| const zoomValueToFitBoundsOnViewport = ( | const zoomValueToFitBoundsOnViewport = ( | ||||||
|   bounds: Bounds, |   bounds: SceneBounds, | ||||||
|   viewportDimensions: { width: number; height: number }, |   viewportDimensions: { width: number; height: number }, | ||||||
| ) => { | ) => { | ||||||
|   const [x1, y1, x2, y2] = bounds; |   const [x1, y1, x2, y2] = bounds; | ||||||
| @@ -235,7 +235,7 @@ export const zoomToFitBounds = ({ | |||||||
|   fitToViewport = false, |   fitToViewport = false, | ||||||
|   viewportZoomFactor = 0.7, |   viewportZoomFactor = 0.7, | ||||||
| }: { | }: { | ||||||
|   bounds: readonly [number, number, number, number]; |   bounds: SceneBounds; | ||||||
|   appState: Readonly<AppState>; |   appState: Readonly<AppState>; | ||||||
|   /** whether to fit content to viewport (beyond >100%) */ |   /** whether to fit content to viewport (beyond >100%) */ | ||||||
|   fitToViewport: boolean; |   fitToViewport: boolean; | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ import { | |||||||
| import { distance2d, rotate, rotatePoint } from "../math"; | import { distance2d, rotate, rotatePoint } from "../math"; | ||||||
| import rough from "roughjs/bin/rough"; | import rough from "roughjs/bin/rough"; | ||||||
| import { Drawable, Op } from "roughjs/bin/core"; | import { Drawable, Op } from "roughjs/bin/core"; | ||||||
| import { Point } from "../types"; | import { AppState, Point } from "../types"; | ||||||
| import { generateRoughOptions } from "../scene/Shape"; | import { generateRoughOptions } from "../scene/Shape"; | ||||||
| import { | import { | ||||||
|   isArrowElement, |   isArrowElement, | ||||||
| @@ -35,7 +35,9 @@ export type RectangleBox = { | |||||||
|  |  | ||||||
| type MaybeQuadraticSolution = [number | null, number | null] | false; | type MaybeQuadraticSolution = [number | null, number | null] | false; | ||||||
|  |  | ||||||
| // x and y position of top left corner, x and y position of bottom right corner | /** | ||||||
|  |  * x and y position of top left corner, x and y position of bottom right corner | ||||||
|  |  */ | ||||||
| export type Bounds = readonly [ | export type Bounds = readonly [ | ||||||
|   minX: number, |   minX: number, | ||||||
|   minY: number, |   minY: number, | ||||||
| @@ -43,6 +45,13 @@ export type Bounds = readonly [ | |||||||
|   maxY: number, |   maxY: number, | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
|  | export type SceneBounds = readonly [ | ||||||
|  |   sceneX: number, | ||||||
|  |   sceneY: number, | ||||||
|  |   sceneX2: number, | ||||||
|  |   sceneY2: number, | ||||||
|  | ]; | ||||||
|  |  | ||||||
| export class ElementBounds { | export class ElementBounds { | ||||||
|   private static boundsCache = new WeakMap< |   private static boundsCache = new WeakMap< | ||||||
|     ExcalidrawElement, |     ExcalidrawElement, | ||||||
| @@ -879,3 +888,21 @@ export const getCommonBoundingBox = ( | |||||||
|     midY: (minY + maxY) / 2, |     midY: (minY + maxY) / 2, | ||||||
|   }; |   }; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * returns scene coords of user's editor viewport (visible canvas area) bounds | ||||||
|  |  */ | ||||||
|  | export const getVisibleSceneBounds = ({ | ||||||
|  |   scrollX, | ||||||
|  |   scrollY, | ||||||
|  |   width, | ||||||
|  |   height, | ||||||
|  |   zoom, | ||||||
|  | }: AppState): SceneBounds => { | ||||||
|  |   return [ | ||||||
|  |     -scrollX, | ||||||
|  |     -scrollY, | ||||||
|  |     -scrollX + width / zoom.value, | ||||||
|  |     -scrollY + height / zoom.value, | ||||||
|  |   ]; | ||||||
|  | }; | ||||||
|   | |||||||
| @@ -249,7 +249,7 @@ export { TTDDialogTrigger } from "./components/TTDDialog/TTDDialogTrigger"; | |||||||
| export { normalizeLink } from "./data/url"; | export { normalizeLink } from "./data/url"; | ||||||
| export { zoomToFitBounds } from "./actions/actionCanvas"; | export { zoomToFitBounds } from "./actions/actionCanvas"; | ||||||
| export { convertToExcalidrawElements } from "./data/transform"; | export { convertToExcalidrawElements } from "./data/transform"; | ||||||
| export { getCommonBounds } from "./element/bounds"; | export { getCommonBounds, getVisibleSceneBounds } from "./element/bounds"; | ||||||
|  |  | ||||||
| export { | export { | ||||||
|   elementsOverlappingBBox, |   elementsOverlappingBBox, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 David Luzar
					David Luzar