Ver Fonte

refactor: simplify zoom by removing `zoom.translation` (#4477)

David Luzar há 3 anos atrás
pai
commit
79d323fab1

+ 31 - 33
src/actions/actionCanvas.tsx

@@ -9,7 +9,7 @@ import { t } from "../i18n";
 import { CODES, KEYS } from "../keys";
 import { getNormalizedZoom, getSelectedElements } from "../scene";
 import { centerScrollOn } from "../scene/scroll";
-import { getNewZoom } from "../scene/zoom";
+import { getStateForZoom } from "../scene/zoom";
 import { AppState, NormalizedZoomValue } from "../types";
 import { getShortcutKey } from "../utils";
 import { register } from "./register";
@@ -73,17 +73,18 @@ export const actionClearCanvas = register({
 
 export const actionZoomIn = register({
   name: "zoomIn",
-  perform: (_elements, appState) => {
-    const zoom = getNewZoom(
-      getNormalizedZoom(appState.zoom.value + ZOOM_STEP),
-      appState.zoom,
-      { left: appState.offsetLeft, top: appState.offsetTop },
-      { x: appState.width / 2, y: appState.height / 2 },
-    );
+  perform: (_elements, appState, _, app) => {
     return {
       appState: {
         ...appState,
-        zoom,
+        ...getStateForZoom(
+          {
+            viewportX: appState.width / 2 + appState.offsetLeft,
+            viewportY: appState.height / 2 + appState.offsetTop,
+            nextZoom: getNormalizedZoom(appState.zoom.value + ZOOM_STEP),
+          },
+          appState,
+        ),
       },
       commitToHistory: false,
     };
@@ -107,18 +108,18 @@ export const actionZoomIn = register({
 
 export const actionZoomOut = register({
   name: "zoomOut",
-  perform: (_elements, appState) => {
-    const zoom = getNewZoom(
-      getNormalizedZoom(appState.zoom.value - ZOOM_STEP),
-      appState.zoom,
-      { left: appState.offsetLeft, top: appState.offsetTop },
-      { x: appState.width / 2, y: appState.height / 2 },
-    );
-
+  perform: (_elements, appState, _, app) => {
     return {
       appState: {
         ...appState,
-        zoom,
+        ...getStateForZoom(
+          {
+            viewportX: appState.width / 2 + appState.offsetLeft,
+            viewportY: appState.height / 2 + appState.offsetTop,
+            nextZoom: getNormalizedZoom(appState.zoom.value - ZOOM_STEP),
+          },
+          appState,
+        ),
       },
       commitToHistory: false,
     };
@@ -142,18 +143,17 @@ export const actionZoomOut = register({
 
 export const actionResetZoom = register({
   name: "resetZoom",
-  perform: (_elements, appState) => {
+  perform: (_elements, appState, _, app) => {
     return {
       appState: {
         ...appState,
-        zoom: getNewZoom(
-          1 as NormalizedZoomValue,
-          appState.zoom,
-          { left: appState.offsetLeft, top: appState.offsetTop },
+        ...getStateForZoom(
           {
-            x: appState.width / 2,
-            y: appState.height / 2,
+            viewportX: appState.width / 2 + appState.offsetLeft,
+            viewportY: appState.height / 2 + appState.offsetTop,
+            nextZoom: getNormalizedZoom(1),
           },
+          appState,
         ),
       },
       commitToHistory: false,
@@ -212,14 +212,12 @@ const zoomToFitElements = (
       ? getCommonBounds(selectedElements)
       : getCommonBounds(nonDeletedElements);
 
-  const zoomValue = zoomValueToFitBoundsOnViewport(commonBounds, {
-    width: appState.width,
-    height: appState.height,
-  });
-  const newZoom = getNewZoom(zoomValue, appState.zoom, {
-    left: appState.offsetLeft,
-    top: appState.offsetTop,
-  });
+  const newZoom = {
+    value: zoomValueToFitBoundsOnViewport(commonBounds, {
+      width: appState.width,
+      height: appState.height,
+    }),
+  };
 
   const [x1, y1, x2, y2] = commonBounds;
   const centerX = (x1 + x2) / 2;

+ 3 - 1
src/appState.ts

@@ -77,7 +77,9 @@ export const getDefaultAppState = (): Omit<
     toastMessage: null,
     viewBackgroundColor: oc.white,
     zenModeEnabled: false,
-    zoom: { value: 1 as NormalizedZoomValue, translation: { x: 0, y: 0 } },
+    zoom: {
+      value: 1 as NormalizedZoomValue,
+    },
     viewModeEnabled: false,
     pendingImageElement: null,
   };

+ 38 - 25
src/components/App.tsx

@@ -177,7 +177,7 @@ import {
 } from "../scene";
 import Scene from "../scene/Scene";
 import { RenderConfig, ScrollBars } from "../scene/types";
-import { getNewZoom } from "../scene/zoom";
+import { getStateForZoom } from "../scene/zoom";
 import { findShapeByKey } from "../shapes";
 import {
   AppClassProperties,
@@ -1880,12 +1880,14 @@ class App extends React.Component<AppProps, AppState> {
 
     const initialScale = gesture.initialScale;
     if (initialScale) {
-      this.setState(({ zoom, offsetLeft, offsetTop }) => ({
-        zoom: getNewZoom(
-          getNormalizedZoom(initialScale * event.scale),
-          zoom,
-          { left: offsetLeft, top: offsetTop },
-          { x: cursorX, y: cursorY },
+      this.setState((state) => ({
+        ...getStateForZoom(
+          {
+            viewportX: cursorX,
+            viewportY: cursorY,
+            nextZoom: getNormalizedZoom(initialScale * event.scale),
+          },
+          state,
         ),
       }));
     }
@@ -2323,17 +2325,27 @@ class App extends React.Component<AppProps, AppState> {
       const distance = getDistance(Array.from(gesture.pointers.values()));
       const scaleFactor = distance / gesture.initialDistance;
 
-      this.setState(({ zoom, scrollX, scrollY, offsetLeft, offsetTop }) => ({
-        scrollX: scrollX + deltaX / zoom.value,
-        scrollY: scrollY + deltaY / zoom.value,
-        zoom: getNewZoom(
-          getNormalizedZoom(initialScale * scaleFactor),
-          zoom,
-          { left: offsetLeft, top: offsetTop },
-          center,
-        ),
-        shouldCacheIgnoreZoom: true,
-      }));
+      const nextZoom = scaleFactor
+        ? getNormalizedZoom(initialScale * scaleFactor)
+        : this.state.zoom.value;
+
+      this.setState((state) => {
+        const zoomState = getStateForZoom(
+          {
+            viewportX: center.x,
+            viewportY: center.y,
+            nextZoom,
+          },
+          state,
+        );
+
+        return {
+          zoom: zoomState.zoom,
+          scrollX: zoomState.scrollX + deltaX / nextZoom,
+          scrollY: zoomState.scrollY + deltaY / nextZoom,
+          shouldCacheIgnoreZoom: true,
+        };
+      });
       this.resetShouldCacheIgnoreZoomDebounced();
     } else {
       gesture.lastCenter =
@@ -2824,6 +2836,7 @@ class App extends React.Component<AppProps, AppState> {
       x: event.clientX,
       y: event.clientY,
     });
+    // console.log("PointerDown", event.pointerId, "size:", gesture.pointers.size);
 
     if (gesture.pointers.size === 2) {
       gesture.lastCenter = getCenter(gesture.pointers);
@@ -2831,6 +2844,7 @@ class App extends React.Component<AppProps, AppState> {
       gesture.initialDistance = getDistance(
         Array.from(gesture.pointers.values()),
       );
+      // console.log("gesture >>>>", gesture);
     }
   }
 
@@ -5111,15 +5125,14 @@ class App extends React.Component<AppProps, AppState> {
       // round to nearest step
       newZoom = Math.round(newZoom * ZOOM_STEP * 100) / (ZOOM_STEP * 100);
 
-      this.setState(({ zoom, offsetLeft, offsetTop }) => ({
-        zoom: getNewZoom(
-          getNormalizedZoom(newZoom),
-          zoom,
-          { left: offsetLeft, top: offsetTop },
+      this.setState((state) => ({
+        ...getStateForZoom(
           {
-            x: cursorX,
-            y: cursorY,
+            viewportX: cursorX,
+            viewportY: cursorY,
+            nextZoom: getNormalizedZoom(newZoom),
           },
+          state,
         ),
         selectedElementIds: {},
         previousSelectedElementIds:

+ 0 - 1
src/data/restore.ts

@@ -261,7 +261,6 @@ export const restoreAppState = (
       typeof appState.zoom === "number"
         ? {
             value: appState.zoom as NormalizedZoomValue,
-            translation: defaultAppState.zoom.translation,
           }
         : appState.zoom || defaultAppState.zoom,
   };

+ 2 - 9
src/renderer/renderScene.ts

@@ -227,10 +227,7 @@ export const renderScene = (
   }
 
   // Apply zoom
-  const zoomTranslationX = renderConfig.zoom.translation.x;
-  const zoomTranslationY = renderConfig.zoom.translation.y;
   context.save();
-  context.translate(zoomTranslationX, zoomTranslationY);
   context.scale(renderConfig.zoom.value, renderConfig.zoom.value);
 
   // Grid
@@ -238,14 +235,10 @@ export const renderScene = (
     strokeGrid(
       context,
       appState.gridSize,
-      -Math.ceil(
-        zoomTranslationX / renderConfig.zoom.value / appState.gridSize,
-      ) *
+      -Math.ceil(renderConfig.zoom.value / appState.gridSize) *
         appState.gridSize +
         (renderConfig.scrollX % appState.gridSize),
-      -Math.ceil(
-        zoomTranslationY / renderConfig.zoom.value / appState.gridSize,
-      ) *
+      -Math.ceil(renderConfig.zoom.value / appState.gridSize) *
         appState.gridSize +
         (renderConfig.scrollY % appState.gridSize),
       normalizedCanvasWidth / renderConfig.zoom.value,

+ 1 - 1
src/scene/index.ts

@@ -18,4 +18,4 @@ export {
   hasText,
   getElementsAtPosition,
 } from "./comparisons";
-export { getNormalizedZoom, getNewZoom } from "./zoom";
+export { getNormalizedZoom } from "./zoom";

+ 2 - 8
src/scene/scroll.ts

@@ -41,14 +41,8 @@ export const centerScrollOn = ({
   zoom: Zoom;
 }) => {
   return {
-    scrollX:
-      (viewportDimensions.width / 2) * (1 / zoom.value) -
-      scenePoint.x -
-      zoom.translation.x * (1 / zoom.value),
-    scrollY:
-      (viewportDimensions.height / 2) * (1 / zoom.value) -
-      scenePoint.y -
-      zoom.translation.y * (1 / zoom.value),
+    scrollX: (viewportDimensions.width / 2) * (1 / zoom.value) - scenePoint.x,
+    scrollY: (viewportDimensions.height / 2) * (1 / zoom.value) - scenePoint.y,
   };
 };
 

+ 35 - 24
src/scene/zoom.ts

@@ -1,30 +1,41 @@
-import { NormalizedZoomValue, PointerCoords, Zoom } from "../types";
-
-export const getNewZoom = (
-  newZoomValue: NormalizedZoomValue,
-  prevZoom: Zoom,
-  canvasOffset: { left: number; top: number },
-  zoomOnViewportPoint: PointerCoords = { x: 0, y: 0 },
-): Zoom => {
-  return {
-    value: newZoomValue,
-    translation: {
-      x:
-        zoomOnViewportPoint.x -
-        canvasOffset.left -
-        (zoomOnViewportPoint.x - canvasOffset.left - prevZoom.translation.x) *
-          (newZoomValue / prevZoom.value),
-      y:
-        zoomOnViewportPoint.y -
-        canvasOffset.top -
-        (zoomOnViewportPoint.y - canvasOffset.top - prevZoom.translation.y) *
-          (newZoomValue / prevZoom.value),
-    },
-  };
-};
+import { AppState, NormalizedZoomValue } from "../types";
 
 export const getNormalizedZoom = (zoom: number): NormalizedZoomValue => {
   const normalizedZoom = parseFloat(zoom.toFixed(2));
   const clampedZoom = Math.max(0.1, Math.min(normalizedZoom, 30));
   return clampedZoom as NormalizedZoomValue;
 };
+
+export const getStateForZoom = (
+  {
+    viewportX,
+    viewportY,
+    nextZoom,
+  }: {
+    viewportX: number;
+    viewportY: number;
+    nextZoom: NormalizedZoomValue;
+  },
+  appState: AppState,
+) => {
+  const appLayerX = viewportX - appState.offsetLeft;
+  const appLayerY = viewportY - appState.offsetTop;
+
+  const currentZoom = appState.zoom.value;
+
+  // get original scroll position without zoom
+  const baseScrollX = appState.scrollX + (appLayerX - appLayerX / currentZoom);
+  const baseScrollY = appState.scrollY + (appLayerY - appLayerY / currentZoom);
+
+  // get scroll offsets for target zoom level
+  const zoomOffsetScrollX = -(appLayerX - appLayerX / nextZoom);
+  const zoomOffsetScrollY = -(appLayerY - appLayerY / nextZoom);
+
+  return {
+    scrollX: baseScrollX + zoomOffsetScrollX,
+    scrollY: baseScrollY + zoomOffsetScrollY,
+    zoom: {
+      value: nextZoom,
+    },
+  };
+};

+ 0 - 64
src/tests/__snapshots__/contextmenu.test.tsx.snap

@@ -72,10 +72,6 @@ Object {
   "width": 200,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -242,10 +238,6 @@ Object {
   "width": 200,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -563,10 +555,6 @@ Object {
   "width": 200,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -884,10 +872,6 @@ Object {
   "width": 200,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -1052,10 +1036,6 @@ Object {
   "width": 200,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -1258,10 +1238,6 @@ Object {
   "width": 200,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -1523,10 +1499,6 @@ Object {
   "width": 200,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -1856,10 +1828,6 @@ Object {
   "width": 200,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -2611,10 +2579,6 @@ Object {
   "width": 200,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -2932,10 +2896,6 @@ Object {
   "width": 200,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -3257,10 +3217,6 @@ Object {
   "width": 200,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -3656,10 +3612,6 @@ Object {
   "width": 200,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -3923,10 +3875,6 @@ Object {
   "width": 200,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -4255,10 +4203,6 @@ Object {
   "width": 200,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -4360,10 +4304,6 @@ Object {
   "width": 200,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -4441,10 +4381,6 @@ Object {
   "width": 200,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }

+ 4 - 212
src/tests/__snapshots__/regressionTests.test.tsx.snap

@@ -83,10 +83,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -572,10 +568,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -1043,10 +1035,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -1858,10 +1846,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -2075,10 +2059,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -2549,10 +2529,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -2809,10 +2785,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -2982,10 +2954,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -3441,10 +3409,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -3690,10 +3654,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -3904,10 +3864,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -4156,10 +4112,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -4419,10 +4371,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -4839,10 +4787,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -5123,10 +5067,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -5428,10 +5368,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -5621,10 +5557,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -5791,10 +5723,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -6272,10 +6200,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -6596,10 +6520,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -8723,10 +8643,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -9101,10 +9017,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -9362,10 +9274,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -9589,10 +9497,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -9875,10 +9779,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -10045,10 +9945,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -10215,10 +10111,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -10385,10 +10277,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -10585,10 +10473,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -10785,10 +10669,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -11003,10 +10883,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -11203,10 +11079,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -11373,10 +11245,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -11543,10 +11411,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -11743,10 +11607,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -11913,10 +11773,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -12142,10 +11998,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -12884,10 +12736,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -13123,7 +12971,7 @@ Object {
   "pendingImageElement": null,
   "previousSelectedElementIds": Object {},
   "resizingElement": null,
-  "scrollX": -5.416666666666667,
+  "scrollX": -2.916666666666668,
   "scrollY": 0,
   "scrolledOutside": false,
   "selectedElementIds": Object {
@@ -13143,10 +12991,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0.4166666666666714,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -13246,10 +13090,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -13354,10 +13194,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -13533,10 +13369,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -13858,10 +13690,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -14077,10 +13905,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -14925,10 +14749,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -15032,10 +14852,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -15849,10 +15665,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -16269,10 +16081,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -16508,8 +16316,8 @@ Object {
   "pendingImageElement": null,
   "previousSelectedElementIds": Object {},
   "resizingElement": null,
-  "scrollX": 11.046099290780141,
-  "scrollY": -5,
+  "scrollX": 10,
+  "scrollY": -10,
   "scrolledOutside": false,
   "selectedElementIds": Object {
     "id0": true,
@@ -16528,11 +16336,7 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": -59.425,
-      "y": -48.66347517730496,
-    },
-    "value": 1.99,
+    "value": 1,
   },
 }
 `;
@@ -16633,10 +16437,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -17148,10 +16948,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }
@@ -17251,10 +17047,6 @@ Object {
   "width": 1024,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }

+ 0 - 4
src/tests/data/restore.test.ts

@@ -415,16 +415,12 @@ describe("restoreAppState", () => {
       );
 
       expect(restoredAppState.zoom.value).toBe(10);
-      expect(restoredAppState.zoom.translation).toMatchObject(
-        getDefaultAppState().zoom.translation,
-      );
     });
 
     it("when the zoom of imported data state is not a number", () => {
       const stubImportedAppState = getDefaultAppState();
       stubImportedAppState.zoom = {
         value: 10 as NormalizedZoomValue,
-        translation: { x: 5, y: 3 },
       };
 
       const stubLocalAppState = getDefaultAppState();

+ 3 - 3
src/tests/helpers/ui.ts

@@ -87,8 +87,8 @@ export class Keyboard {
 }
 
 export class Pointer {
-  private clientX = 0;
-  private clientY = 0;
+  public clientX = 0;
+  public clientY = 0;
 
   constructor(
     private readonly pointerType: "mouse" | "touch" | "pen",
@@ -156,7 +156,7 @@ export class Pointer {
   // absolute coords
   // ---------------------------------------------------------------------------
 
-  moveTo(x: number, y: number) {
+  moveTo(x: number = this.clientX, y: number = this.clientY) {
     this.clientX = x;
     this.clientY = y;
     fireEvent.pointerMove(GlobalTestState.canvas, this.getEvent());

+ 0 - 4
src/tests/packages/__snapshots__/utils.test.ts.snap

@@ -67,10 +67,6 @@ Object {
   "viewModeEnabled": false,
   "zenModeEnabled": false,
   "zoom": Object {
-    "translation": Object {
-      "x": 0,
-      "y": 0,
-    },
     "value": 1,
   },
 }

+ 27 - 10
src/tests/regressionTests.test.tsx

@@ -287,22 +287,39 @@ describe("regression tests", () => {
   });
 
   it("two-finger scroll works", () => {
+    // scroll horizontally vertically
+
     const startScrollY = h.state.scrollY;
-    finger1.down(50, 50);
-    finger2.down(60, 50);
 
-    finger1.up(0, -10);
-    finger2.up(0, -10);
+    finger1.downAt(0, 0);
+    finger2.downAt(10, 0);
+
+    finger1.clientY -= 10;
+    finger2.clientY -= 10;
+
+    finger1.moveTo();
+    finger2.moveTo();
+
+    finger1.upAt();
+    finger2.upAt();
     expect(h.state.scrollY).toBeLessThan(startScrollY);
 
+    // scroll horizontally
+
     const startScrollX = h.state.scrollX;
 
-    finger1.restorePosition(50, 50);
-    finger2.restorePosition(50, 60);
-    finger1.down();
-    finger2.down();
-    finger1.up(10, 0);
-    finger2.up(10, 0);
+    finger1.downAt();
+    finger2.downAt();
+
+    finger1.clientX += 10;
+    finger2.clientX += 10;
+
+    finger1.moveTo();
+    finger2.moveTo();
+
+    finger1.upAt();
+    finger2.upAt();
+
     expect(h.state.scrollX).toBeGreaterThan(startScrollX);
   });
 

+ 0 - 4
src/types.ts

@@ -155,10 +155,6 @@ export type NormalizedZoomValue = number & { _brand: "normalizedZoom" };
 
 export type Zoom = Readonly<{
   value: NormalizedZoomValue;
-  translation: Readonly<{
-    x: number;
-    y: number;
-  }>;
 }>;
 
 export type PointerCoords = Readonly<{

+ 5 - 4
src/utils.ts

@@ -223,8 +223,9 @@ export const viewportCoordsToSceneCoords = (
   },
 ) => {
   const invScale = 1 / zoom.value;
-  const x = (clientX - zoom.translation.x - offsetLeft) * invScale - scrollX;
-  const y = (clientY - zoom.translation.y - offsetTop) * invScale - scrollY;
+  const x = (clientX - offsetLeft) * invScale - scrollX;
+  const y = (clientY - offsetTop) * invScale - scrollY;
+
   return { x, y };
 };
 
@@ -244,8 +245,8 @@ export const sceneCoordsToViewportCoords = (
     scrollY: number;
   },
 ) => {
-  const x = (sceneX + scrollX) * zoom.value + zoom.translation.x + offsetLeft;
-  const y = (sceneY + scrollY) * zoom.value + zoom.translation.y + offsetTop;
+  const x = (sceneX + scrollX) * zoom.value + offsetLeft;
+  const y = (sceneY + scrollY) * zoom.value + offsetTop;
   return { x, y };
 };