diff --git a/src/element/bounds.ts b/src/element/bounds.ts index 6b760f7e..d3d259bf 100644 --- a/src/element/bounds.ts +++ b/src/element/bounds.ts @@ -57,3 +57,20 @@ export function getLinePoints(element: ExcalidrawElement) { return [x1, y1, x2, y2]; } + +export function getCommonBounds(elements: readonly ExcalidrawElement[]) { + let minX = Infinity; + let maxX = -Infinity; + let minY = Infinity; + let maxY = -Infinity; + + elements.forEach(element => { + const [x1, y1, x2, y2] = getElementAbsoluteCoords(element); + minX = Math.min(minX, x1); + minY = Math.min(minY, y1); + maxX = Math.max(maxX, x2); + maxY = Math.max(maxY, y2); + }); + + return [minX, minY, maxX, maxY]; +} diff --git a/src/element/index.ts b/src/element/index.ts index a31a0e4b..62ed2125 100644 --- a/src/element/index.ts +++ b/src/element/index.ts @@ -1,6 +1,7 @@ export { newElement, newTextElement, duplicateElement } from "./newElement"; export { getElementAbsoluteCoords, + getCommonBounds, getDiamondPoints, getArrowPoints, getLinePoints, diff --git a/src/index.tsx b/src/index.tsx index 2360f201..ecf9ceea 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -13,7 +13,7 @@ import { isInvisiblySmallElement, isTextElement, textWysiwyg, - getElementAbsoluteCoords, + getCommonBounds, getCursorForResizingElement, getPerfectElementSize, resizePerfectLineForNWHandler, @@ -1388,24 +1388,10 @@ export class App extends React.Component { ) { elements = clearSelection(elements); - let subCanvasX1 = Infinity; - let subCanvasX2 = -Infinity; - let subCanvasY1 = Infinity; - let subCanvasY2 = -Infinity; + const [minX, minY, maxX, maxY] = getCommonBounds(parsedElements); - const minX = Math.min(...parsedElements.map(element => element.x)); - const minY = Math.min(...parsedElements.map(element => element.y)); - - parsedElements.forEach(parsedElement => { - const [x1, y1, x2, y2] = getElementAbsoluteCoords(parsedElement); - subCanvasX1 = Math.min(subCanvasX1, x1); - subCanvasY1 = Math.min(subCanvasY1, y1); - subCanvasX2 = Math.max(subCanvasX2, x2); - subCanvasY2 = Math.max(subCanvasY2, y2); - }); - - const elementsCenterX = distance(subCanvasX1, subCanvasX2) / 2; - const elementsCenterY = distance(subCanvasY1, subCanvasY2) / 2; + const elementsCenterX = distance(minX, maxX) / 2; + const elementsCenterY = distance(minY, maxY) / 2; const dx = cursorX - diff --git a/src/scene/data.ts b/src/scene/data.ts index 02c34695..9c2aadfb 100644 --- a/src/scene/data.ts +++ b/src/scene/data.ts @@ -7,6 +7,7 @@ import { ExportType } from "./types"; import { getExportCanvasPreview } from "./getExportCanvasPreview"; import nanoid from "nanoid"; import { fileOpen, fileSave } from "browser-nativefs"; +import { getCommonBounds } from "../element"; import i18n from "../i18n"; @@ -43,35 +44,14 @@ export function serializeAsJSON( ); } -function calculateScroll( +function calculateScrollCenter( elements: readonly ExcalidrawElement[], ): { scrollX: number; scrollY: number } { - // Bounding box of all elements - let top = Number.MAX_SAFE_INTEGER; - let left = Number.MAX_SAFE_INTEGER; - let bottom = -Number.MAX_SAFE_INTEGER; - let right = -Number.MAX_SAFE_INTEGER; + let [x1, y1, x2, y2] = getCommonBounds(elements); + + const centerX = (x1 + x2) / 2; + const centerY = (y1 + y2) / 2; - for (const element of elements) { - left = Math.min( - left, - element.width > 0 ? element.x : element.x + element.width, - ); - top = Math.min( - top, - element.height > 0 ? element.y : element.y + element.height, - ); - right = Math.max( - right, - element.width > 0 ? element.x + element.width : element.x, - ); - bottom = Math.max( - bottom, - element.height > 0 ? element.y + element.height : element.y, - ); - } - const centerX = left + (right - left) / 2; - const centerY = top + (bottom - top) / 2; return { scrollX: window.innerWidth / 2 - centerX, scrollY: window.innerHeight / 2 - centerY, @@ -136,7 +116,9 @@ export async function loadFromJSON() { } const { elements, appState } = updateAppState(contents); return new Promise(resolve => { - resolve(restore(elements, { ...appState, ...calculateScroll(elements) })); + resolve( + restore(elements, { ...appState, ...calculateScrollCenter(elements) }), + ); }); } @@ -187,7 +169,7 @@ export async function importFromBackend(id: string | null) { console.error(error); } } - return restore(elements, { ...appState, ...calculateScroll(elements) }); + return restore(elements, { ...appState, ...calculateScrollCenter(elements) }); } export async function exportCanvas( diff --git a/src/scene/getExportCanvasPreview.ts b/src/scene/getExportCanvasPreview.ts index c34bf2ce..b279a1a8 100644 --- a/src/scene/getExportCanvasPreview.ts +++ b/src/scene/getExportCanvasPreview.ts @@ -1,6 +1,6 @@ import rough from "roughjs/bin/rough"; import { ExcalidrawElement } from "../element/types"; -import { getElementAbsoluteCoords } from "../element/bounds"; +import { getCommonBounds } from "../element/bounds"; import { renderScene } from "../renderer/renderScene"; import { distance } from "../utils"; @@ -28,21 +28,10 @@ export function getExportCanvasPreview( }, ) { // calculate smallest area to fit the contents in - let subCanvasX1 = Infinity; - let subCanvasX2 = -Infinity; - let subCanvasY1 = Infinity; - let subCanvasY2 = -Infinity; + const [minX, minY, maxX, maxY] = getCommonBounds(elements); + const width = distance(minX, maxX) + exportPadding * 2; + const height = distance(minY, maxY) + exportPadding * 2; - elements.forEach(element => { - const [x1, y1, x2, y2] = getElementAbsoluteCoords(element); - subCanvasX1 = Math.min(subCanvasX1, x1); - subCanvasY1 = Math.min(subCanvasY1, y1); - subCanvasX2 = Math.max(subCanvasX2, x2); - subCanvasY2 = Math.max(subCanvasY2, y2); - }); - - const width = distance(subCanvasX1, subCanvasX2) + exportPadding * 2; - const height = distance(subCanvasY1, subCanvasY2) + exportPadding * 2; const tempCanvas: any = createCanvas(width, height); tempCanvas.getContext("2d")?.scale(scale, scale); @@ -56,8 +45,8 @@ export function getExportCanvasPreview( scrollY: 0, }, { - offsetX: -subCanvasX1 + exportPadding, - offsetY: -subCanvasY1 + exportPadding, + offsetX: -minX + exportPadding, + offsetY: -minY + exportPadding, renderScrollbars: false, renderSelection: false, }, diff --git a/src/scene/scrollbars.ts b/src/scene/scrollbars.ts index 144f25c1..fdcf609a 100644 --- a/src/scene/scrollbars.ts +++ b/src/scene/scrollbars.ts @@ -1,5 +1,5 @@ import { ExcalidrawElement } from "../element/types"; -import { getElementAbsoluteCoords } from "../element"; +import { getCommonBounds } from "../element"; const SCROLLBAR_MIN_SIZE = 15; const SCROLLBAR_MARGIN = 4; @@ -13,23 +13,13 @@ export function getScrollBars( scrollX: number, scrollY: number, ) { - let minX = Infinity; - let maxX = -Infinity; - let minY = Infinity; - let maxY = -Infinity; - - elements.forEach(element => { - const [x1, y1, x2, y2] = getElementAbsoluteCoords(element); - minX = Math.min(minX, x1); - minY = Math.min(minY, y1); - maxX = Math.max(maxX, x2); - maxY = Math.max(maxY, y2); - }); + let [minX, minY, maxX, maxY] = getCommonBounds(elements); minX += scrollX; maxX += scrollX; minY += scrollY; maxY += scrollY; + const leftOverflow = Math.max(-minX, 0); const rightOverflow = Math.max(-(canvasWidth - maxX), 0); const topOverflow = Math.max(-minY, 0); diff --git a/src/utils.ts b/src/utils.ts index d473cb47..5f6b7965 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -88,5 +88,5 @@ export function removeSelection() { } export function distance(x: number, y: number) { - return Math.abs(x > y ? x - y : y - x); + return Math.abs(x - y); }