Procházet zdrojové kódy

fix: scrollbar rendering and improve dragging (#9417)

* fix: scrollbar rendering and improve dragging

* tweak offsets
David Luzar před 3 měsíci
rodič
revize
8fb2f70414

+ 8 - 2
packages/excalidraw/components/App.tsx

@@ -8787,7 +8787,10 @@ class App extends React.Component<AppProps, AppState> {
       const x = event.clientX;
       const dx = x - pointerDownState.lastCoords.x;
       this.translateCanvas({
-        scrollX: this.state.scrollX - dx / this.state.zoom.value,
+        scrollX:
+          this.state.scrollX -
+          (dx * (currentScrollBars.horizontal?.deltaMultiplier || 1)) /
+            this.state.zoom.value,
       });
       pointerDownState.lastCoords.x = x;
       return true;
@@ -8797,7 +8800,10 @@ class App extends React.Component<AppProps, AppState> {
       const y = event.clientY;
       const dy = y - pointerDownState.lastCoords.y;
       this.translateCanvas({
-        scrollY: this.state.scrollY - dy / this.state.zoom.value,
+        scrollY:
+          this.state.scrollY -
+          (dy * (currentScrollBars.vertical?.deltaMultiplier || 1)) /
+            this.state.zoom.value,
       });
       pointerDownState.lastCoords.y = y;
       return true;

+ 50 - 21
packages/excalidraw/scene/scrollbars.ts

@@ -11,6 +11,7 @@ export const SCROLLBAR_MARGIN = 4;
 export const SCROLLBAR_WIDTH = 6;
 export const SCROLLBAR_COLOR = "rgba(0,0,0,0.3)";
 
+// The scrollbar represents where the viewport is in relationship to the scene
 export const getScrollBars = (
   elements: RenderableElementsMap,
   viewportWidth: number,
@@ -31,9 +32,6 @@ export const getScrollBars = (
   const viewportWidthWithZoom = viewportWidth / appState.zoom.value;
   const viewportHeightWithZoom = viewportHeight / appState.zoom.value;
 
-  const viewportWidthDiff = viewportWidth - viewportWidthWithZoom;
-  const viewportHeightDiff = viewportHeight - viewportHeightWithZoom;
-
   const safeArea = {
     top: parseInt(getGlobalCSSVariable("sat")) || 0,
     bottom: parseInt(getGlobalCSSVariable("sab")) || 0,
@@ -44,10 +42,8 @@ export const getScrollBars = (
   const isRTL = getLanguage().rtl;
 
   // The viewport is the rectangle currently visible for the user
-  const viewportMinX =
-    -appState.scrollX + viewportWidthDiff / 2 + safeArea.left;
-  const viewportMinY =
-    -appState.scrollY + viewportHeightDiff / 2 + safeArea.top;
+  const viewportMinX = -appState.scrollX + safeArea.left;
+  const viewportMinY = -appState.scrollY + safeArea.top;
   const viewportMaxX = viewportMinX + viewportWidthWithZoom - safeArea.right;
   const viewportMaxY = viewportMinY + viewportHeightWithZoom - safeArea.bottom;
 
@@ -57,8 +53,43 @@ export const getScrollBars = (
   const sceneMaxX = Math.max(elementsMaxX, viewportMaxX);
   const sceneMaxY = Math.max(elementsMaxY, viewportMaxY);
 
-  // The scrollbar represents where the viewport is in relationship to the scene
+  // the elements-only bbox
+  const sceneWidth = elementsMaxX - elementsMinX;
+  const sceneHeight = elementsMaxY - elementsMinY;
+
+  // scene (elements) bbox + the viewport bbox that extends outside of it
+  const extendedSceneWidth = sceneMaxX - sceneMinX;
+  const extendedSceneHeight = sceneMaxY - sceneMinY;
+
+  const scrollWidthOffset =
+    Math.max(SCROLLBAR_MARGIN * 2, safeArea.left + safeArea.right) +
+    SCROLLBAR_WIDTH * 2;
+
+  const scrollbarWidth =
+    viewportWidth * (viewportWidthWithZoom / extendedSceneWidth) -
+    scrollWidthOffset;
 
+  const scrollbarHeightOffset =
+    Math.max(SCROLLBAR_MARGIN * 2, safeArea.top + safeArea.bottom) +
+    SCROLLBAR_WIDTH * 2;
+
+  const scrollbarHeight =
+    viewportHeight * (viewportHeightWithZoom / extendedSceneHeight) -
+    scrollbarHeightOffset;
+  // NOTE the delta multiplier calculation isn't quite correct when viewport
+  // is extended outside the scene (elements) bbox as there's some small
+  // accumulation error. I'll let this be an exercise for others to fix. ^^
+  const horizontalDeltaMultiplier =
+    extendedSceneWidth > sceneWidth
+      ? (extendedSceneWidth * appState.zoom.value) /
+        (scrollbarWidth + scrollWidthOffset)
+      : viewportWidth / (scrollbarWidth + scrollWidthOffset);
+
+  const verticalDeltaMultiplier =
+    extendedSceneHeight > sceneHeight
+      ? (extendedSceneHeight * appState.zoom.value) /
+        (scrollbarHeight + scrollbarHeightOffset)
+      : viewportHeight / (scrollbarHeight + scrollbarHeightOffset);
   return {
     horizontal:
       viewportMinX === sceneMinX && viewportMaxX === sceneMaxX
@@ -66,18 +97,17 @@ export const getScrollBars = (
         : {
             x:
               Math.max(safeArea.left, SCROLLBAR_MARGIN) +
-              ((viewportMinX - sceneMinX) / (sceneMaxX - sceneMinX)) *
-                viewportWidth,
+              SCROLLBAR_WIDTH +
+              ((viewportMinX - sceneMinX) / extendedSceneWidth) * viewportWidth,
             y:
               viewportHeight -
               SCROLLBAR_WIDTH -
               Math.max(SCROLLBAR_MARGIN, safeArea.bottom),
-            width:
-              ((viewportMaxX - viewportMinX) / (sceneMaxX - sceneMinX)) *
-                viewportWidth -
-              Math.max(SCROLLBAR_MARGIN * 2, safeArea.left + safeArea.right),
+            width: scrollbarWidth,
             height: SCROLLBAR_WIDTH,
+            deltaMultiplier: horizontalDeltaMultiplier,
           },
+
     vertical:
       viewportMinY === sceneMinY && viewportMaxY === sceneMaxY
         ? null
@@ -88,14 +118,13 @@ export const getScrollBars = (
                 SCROLLBAR_WIDTH -
                 Math.max(safeArea.right, SCROLLBAR_MARGIN),
             y:
-              ((viewportMinY - sceneMinY) / (sceneMaxY - sceneMinY)) *
-                viewportHeight +
-              Math.max(safeArea.top, SCROLLBAR_MARGIN),
+              Math.max(safeArea.top, SCROLLBAR_MARGIN) +
+              SCROLLBAR_WIDTH +
+              ((viewportMinY - sceneMinY) / extendedSceneHeight) *
+                viewportHeight,
             width: SCROLLBAR_WIDTH,
-            height:
-              ((viewportMaxY - viewportMinY) / (sceneMaxY - sceneMinY)) *
-                viewportHeight -
-              Math.max(SCROLLBAR_MARGIN * 2, safeArea.top + safeArea.bottom),
+            height: scrollbarHeight,
+            deltaMultiplier: verticalDeltaMultiplier,
           },
   };
 };

+ 2 - 0
packages/excalidraw/scene/types.ts

@@ -130,12 +130,14 @@ export type ScrollBars = {
     y: number;
     width: number;
     height: number;
+    deltaMultiplier: number;
   } | null;
   vertical: {
     x: number;
     y: number;
     width: number;
     height: number;
+    deltaMultiplier: number;
   } | null;
 };