|
@@ -1,67 +1,70 @@
|
|
|
import { ExcalidrawElement } from "../element/types";
|
|
|
import { getCommonBounds } from "../element";
|
|
|
|
|
|
-const SCROLLBAR_MIN_SIZE = 15;
|
|
|
const SCROLLBAR_MARGIN = 4;
|
|
|
export const SCROLLBAR_WIDTH = 6;
|
|
|
export const SCROLLBAR_COLOR = "rgba(0,0,0,0.3)";
|
|
|
|
|
|
export function getScrollBars(
|
|
|
elements: readonly ExcalidrawElement[],
|
|
|
- canvasWidth: number,
|
|
|
- canvasHeight: number,
|
|
|
+ viewportWidth: number,
|
|
|
+ viewportHeight: number,
|
|
|
scrollX: number,
|
|
|
scrollY: number,
|
|
|
) {
|
|
|
- let [minX, minY, maxX, maxY] = getCommonBounds(elements);
|
|
|
+ // This is the bounding box of all the elements
|
|
|
+ const [
|
|
|
+ elementsMinX,
|
|
|
+ elementsMinY,
|
|
|
+ elementsMaxX,
|
|
|
+ elementsMaxY,
|
|
|
+ ] = getCommonBounds(elements);
|
|
|
|
|
|
- minX += scrollX;
|
|
|
- maxX += scrollX;
|
|
|
- minY += scrollY;
|
|
|
- maxY += scrollY;
|
|
|
+ // The viewport is the rectangle currently visible for the user
|
|
|
+ const viewportMinX = -scrollX;
|
|
|
+ const viewportMinY = -scrollY;
|
|
|
+ const viewportMaxX = -scrollX + viewportWidth;
|
|
|
+ const viewportMaxY = -scrollY + viewportHeight;
|
|
|
|
|
|
- const leftOverflow = Math.max(-minX, 0);
|
|
|
- const rightOverflow = Math.max(-(canvasWidth - maxX), 0);
|
|
|
- const topOverflow = Math.max(-minY, 0);
|
|
|
- const bottomOverflow = Math.max(-(canvasHeight - maxY), 0);
|
|
|
+ // The scene is the bounding box of both the elements and viewport
|
|
|
+ const sceneMinX = Math.min(elementsMinX, viewportMinX);
|
|
|
+ const sceneMinY = Math.min(elementsMinY, viewportMinY);
|
|
|
+ const sceneMaxX = Math.max(elementsMaxX, viewportMaxX);
|
|
|
+ const sceneMaxY = Math.max(elementsMaxY, viewportMaxY);
|
|
|
|
|
|
- // horizontal scrollbar
|
|
|
- let horizontalScrollBar = null;
|
|
|
- if (leftOverflow || rightOverflow) {
|
|
|
- horizontalScrollBar = {
|
|
|
- x: Math.min(
|
|
|
- leftOverflow + SCROLLBAR_MARGIN,
|
|
|
- canvasWidth - SCROLLBAR_MIN_SIZE - SCROLLBAR_MARGIN,
|
|
|
- ),
|
|
|
- y: canvasHeight - SCROLLBAR_WIDTH - SCROLLBAR_MARGIN,
|
|
|
- width: Math.max(
|
|
|
- canvasWidth - rightOverflow - leftOverflow - SCROLLBAR_MARGIN * 2,
|
|
|
- SCROLLBAR_MIN_SIZE,
|
|
|
- ),
|
|
|
- height: SCROLLBAR_WIDTH,
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- // vertical scrollbar
|
|
|
- let verticalScrollBar = null;
|
|
|
- if (topOverflow || bottomOverflow) {
|
|
|
- verticalScrollBar = {
|
|
|
- x: canvasWidth - SCROLLBAR_WIDTH - SCROLLBAR_MARGIN,
|
|
|
- y: Math.min(
|
|
|
- topOverflow + SCROLLBAR_MARGIN,
|
|
|
- canvasHeight - SCROLLBAR_MIN_SIZE - SCROLLBAR_MARGIN,
|
|
|
- ),
|
|
|
- width: SCROLLBAR_WIDTH,
|
|
|
- height: Math.max(
|
|
|
- canvasHeight - bottomOverflow - topOverflow - SCROLLBAR_WIDTH * 2,
|
|
|
- SCROLLBAR_MIN_SIZE,
|
|
|
- ),
|
|
|
- };
|
|
|
- }
|
|
|
+ // The scrollbar represents where the viewport is in relationship to the scene
|
|
|
|
|
|
return {
|
|
|
- horizontal: horizontalScrollBar,
|
|
|
- vertical: verticalScrollBar,
|
|
|
+ horizontal:
|
|
|
+ viewportMinX === sceneMinX && viewportMaxX === sceneMaxX
|
|
|
+ ? null
|
|
|
+ : {
|
|
|
+ x:
|
|
|
+ ((viewportMinX - sceneMinX) / (sceneMaxX - sceneMinX)) *
|
|
|
+ viewportWidth +
|
|
|
+ SCROLLBAR_MARGIN,
|
|
|
+ y: viewportHeight - SCROLLBAR_WIDTH - SCROLLBAR_MARGIN,
|
|
|
+ width:
|
|
|
+ ((viewportMaxX - viewportMinX) / (sceneMaxX - sceneMinX)) *
|
|
|
+ viewportWidth -
|
|
|
+ SCROLLBAR_MARGIN * 2,
|
|
|
+ height: SCROLLBAR_WIDTH,
|
|
|
+ },
|
|
|
+ vertical:
|
|
|
+ viewportMinY === sceneMinY && viewportMaxY === sceneMaxY
|
|
|
+ ? null
|
|
|
+ : {
|
|
|
+ x: viewportWidth - SCROLLBAR_WIDTH - SCROLLBAR_MARGIN,
|
|
|
+ y:
|
|
|
+ ((viewportMinY - sceneMinY) / (sceneMaxY - sceneMinY)) *
|
|
|
+ viewportHeight +
|
|
|
+ SCROLLBAR_MARGIN,
|
|
|
+ width: SCROLLBAR_WIDTH,
|
|
|
+ height:
|
|
|
+ ((viewportMaxY - viewportMinY) / (sceneMaxY - sceneMinY)) *
|
|
|
+ viewportHeight -
|
|
|
+ SCROLLBAR_MARGIN * 2,
|
|
|
+ },
|
|
|
};
|
|
|
}
|
|
|
|
|
@@ -69,15 +72,15 @@ export function isOverScrollBars(
|
|
|
elements: readonly ExcalidrawElement[],
|
|
|
x: number,
|
|
|
y: number,
|
|
|
- canvasWidth: number,
|
|
|
- canvasHeight: number,
|
|
|
+ viewportWidth: number,
|
|
|
+ viewportHeight: number,
|
|
|
scrollX: number,
|
|
|
scrollY: number,
|
|
|
) {
|
|
|
const scrollBars = getScrollBars(
|
|
|
elements,
|
|
|
- canvasWidth,
|
|
|
- canvasHeight,
|
|
|
+ viewportWidth,
|
|
|
+ viewportHeight,
|
|
|
scrollX,
|
|
|
scrollY,
|
|
|
);
|
|
@@ -85,14 +88,15 @@ export function isOverScrollBars(
|
|
|
const [isOverHorizontalScrollBar, isOverVerticalScrollBar] = [
|
|
|
scrollBars.horizontal,
|
|
|
scrollBars.vertical,
|
|
|
- ].map(
|
|
|
- scrollBar =>
|
|
|
+ ].map(scrollBar => {
|
|
|
+ return (
|
|
|
scrollBar &&
|
|
|
scrollBar.x <= x &&
|
|
|
x <= scrollBar.x + scrollBar.width &&
|
|
|
scrollBar.y <= y &&
|
|
|
- y <= scrollBar.y + scrollBar.height,
|
|
|
- );
|
|
|
+ y <= scrollBar.y + scrollBar.height
|
|
|
+ );
|
|
|
+ });
|
|
|
|
|
|
return {
|
|
|
isOverHorizontalScrollBar,
|