diff options
| author | kj_sh604 | 2026-03-15 16:19:35 -0400 |
|---|---|---|
| committer | kj_sh604 | 2026-03-15 16:19:35 -0400 |
| commit | 6ec259a0e71174651bae95d4628138bf6fd68742 (patch) | |
| tree | 5e33c6a5ec091ecabfcb257fdc7b6a88ed8754ac /packages/excalidraw/scene/scroll.ts | |
| parent | 16c8578b15c727f22921f8a80a56ee4d4e7f2272 (diff) | |
refactor: packages/
Diffstat (limited to 'packages/excalidraw/scene/scroll.ts')
| -rw-r--r-- | packages/excalidraw/scene/scroll.ts | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/packages/excalidraw/scene/scroll.ts b/packages/excalidraw/scene/scroll.ts new file mode 100644 index 0000000..5d059e5 --- /dev/null +++ b/packages/excalidraw/scene/scroll.ts @@ -0,0 +1,91 @@ +import type { AppState, Offsets, PointerCoords, Zoom } from "../types"; +import type { ExcalidrawElement } from "../element/types"; +import { + getCommonBounds, + getClosestElementBounds, + getVisibleElements, +} from "../element"; + +import { + sceneCoordsToViewportCoords, + viewportCoordsToSceneCoords, +} from "../utils"; + +const isOutsideViewPort = (appState: AppState, cords: Array<number>) => { + const [x1, y1, x2, y2] = cords; + const { x: viewportX1, y: viewportY1 } = sceneCoordsToViewportCoords( + { sceneX: x1, sceneY: y1 }, + appState, + ); + const { x: viewportX2, y: viewportY2 } = sceneCoordsToViewportCoords( + { sceneX: x2, sceneY: y2 }, + appState, + ); + return ( + viewportX2 - viewportX1 > appState.width || + viewportY2 - viewportY1 > appState.height + ); +}; + +export const centerScrollOn = ({ + scenePoint, + viewportDimensions, + zoom, + offsets, +}: { + scenePoint: PointerCoords; + viewportDimensions: { height: number; width: number }; + zoom: Zoom; + offsets?: Offsets; +}) => { + let scrollX = + (viewportDimensions.width - (offsets?.right ?? 0)) / 2 / zoom.value - + scenePoint.x; + + scrollX += (offsets?.left ?? 0) / 2 / zoom.value; + + let scrollY = + (viewportDimensions.height - (offsets?.bottom ?? 0)) / 2 / zoom.value - + scenePoint.y; + + scrollY += (offsets?.top ?? 0) / 2 / zoom.value; + + return { + scrollX, + scrollY, + }; +}; + +export const calculateScrollCenter = ( + elements: readonly ExcalidrawElement[], + appState: AppState, +): { scrollX: number; scrollY: number } => { + elements = getVisibleElements(elements); + + if (!elements.length) { + return { + scrollX: 0, + scrollY: 0, + }; + } + let [x1, y1, x2, y2] = getCommonBounds(elements); + + if (isOutsideViewPort(appState, [x1, y1, x2, y2])) { + [x1, y1, x2, y2] = getClosestElementBounds( + elements, + viewportCoordsToSceneCoords( + { clientX: appState.scrollX, clientY: appState.scrollY }, + appState, + ), + ); + } + + const centerX = (x1 + x2) / 2; + const centerY = (y1 + y2) / 2; + + return centerScrollOn({ + scenePoint: { x: centerX, y: centerY }, + viewportDimensions: { width: appState.width, height: appState.height }, + zoom: appState.zoom, + }); +}; |
