From 79d323fab154147c24c3a5d4b33fcc37ed85221f Mon Sep 17 00:00:00 2001 From: David Luzar Date: Sat, 29 Jan 2022 21:12:44 +0100 Subject: [PATCH] refactor: simplify zoom by removing `zoom.translation` (#4477) --- src/actions/actionCanvas.tsx | 64 +++--- src/appState.ts | 4 +- src/components/App.tsx | 63 +++-- src/data/restore.ts | 1 - src/renderer/renderScene.ts | 11 +- src/scene/index.ts | 2 +- src/scene/scroll.ts | 10 +- src/scene/zoom.ts | 59 +++-- .../__snapshots__/contextmenu.test.tsx.snap | 64 ------ .../regressionTests.test.tsx.snap | 216 +----------------- src/tests/data/restore.test.ts | 4 - src/tests/helpers/ui.ts | 6 +- .../packages/__snapshots__/utils.test.ts.snap | 4 - src/tests/regressionTests.test.tsx | 39 +++- src/types.ts | 4 - src/utils.ts | 9 +- 16 files changed, 152 insertions(+), 408 deletions(-) diff --git a/src/actions/actionCanvas.tsx b/src/actions/actionCanvas.tsx index 7c3f9e23..ff937d98 100644 --- a/src/actions/actionCanvas.tsx +++ b/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; diff --git a/src/appState.ts b/src/appState.ts index a15a4e95..d226b808 100644 --- a/src/appState.ts +++ b/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, }; diff --git a/src/components/App.tsx b/src/components/App.tsx index cfe32c6f..61e6a8a4 100644 --- a/src/components/App.tsx +++ b/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 { 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 { 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 { 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 { gesture.initialDistance = getDistance( Array.from(gesture.pointers.values()), ); + // console.log("gesture >>>>", gesture); } } @@ -5111,15 +5125,14 @@ class App extends React.Component { // 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: diff --git a/src/data/restore.ts b/src/data/restore.ts index 68b26992..f9e03b6a 100644 --- a/src/data/restore.ts +++ b/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, }; diff --git a/src/renderer/renderScene.ts b/src/renderer/renderScene.ts index 1283c07c..dbf735e1 100644 --- a/src/renderer/renderScene.ts +++ b/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, diff --git a/src/scene/index.ts b/src/scene/index.ts index 27e2596f..0b995664 100644 --- a/src/scene/index.ts +++ b/src/scene/index.ts @@ -18,4 +18,4 @@ export { hasText, getElementsAtPosition, } from "./comparisons"; -export { getNormalizedZoom, getNewZoom } from "./zoom"; +export { getNormalizedZoom } from "./zoom"; diff --git a/src/scene/scroll.ts b/src/scene/scroll.ts index b2fe46a4..4fce5e64 100644 --- a/src/scene/scroll.ts +++ b/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, }; }; diff --git a/src/scene/zoom.ts b/src/scene/zoom.ts index 6ca292e0..2832a8bc 100644 --- a/src/scene/zoom.ts +++ b/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, + }, + }; +}; diff --git a/src/tests/__snapshots__/contextmenu.test.tsx.snap b/src/tests/__snapshots__/contextmenu.test.tsx.snap index 6cdb9128..55e335ab 100644 --- a/src/tests/__snapshots__/contextmenu.test.tsx.snap +++ b/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, }, } diff --git a/src/tests/__snapshots__/regressionTests.test.tsx.snap b/src/tests/__snapshots__/regressionTests.test.tsx.snap index db89ff19..b061db83 100644 --- a/src/tests/__snapshots__/regressionTests.test.tsx.snap +++ b/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, }, } diff --git a/src/tests/data/restore.test.ts b/src/tests/data/restore.test.ts index 5e88ee98..ce010f81 100644 --- a/src/tests/data/restore.test.ts +++ b/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(); diff --git a/src/tests/helpers/ui.ts b/src/tests/helpers/ui.ts index 8e70a8c0..b951710f 100644 --- a/src/tests/helpers/ui.ts +++ b/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()); diff --git a/src/tests/packages/__snapshots__/utils.test.ts.snap b/src/tests/packages/__snapshots__/utils.test.ts.snap index 88f98d5b..531a59aa 100644 --- a/src/tests/packages/__snapshots__/utils.test.ts.snap +++ b/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, }, } diff --git a/src/tests/regressionTests.test.tsx b/src/tests/regressionTests.test.tsx index 478f095a..7b950e3d 100644 --- a/src/tests/regressionTests.test.tsx +++ b/src/tests/regressionTests.test.tsx @@ -287,22 +287,39 @@ describe("regression tests", () => { }); it("two-finger scroll works", () => { - const startScrollY = h.state.scrollY; - finger1.down(50, 50); - finger2.down(60, 50); + // scroll horizontally vertically - finger1.up(0, -10); - finger2.up(0, -10); + const startScrollY = h.state.scrollY; + + 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); }); diff --git a/src/types.ts b/src/types.ts index c6908c04..79951c7b 100644 --- a/src/types.ts +++ b/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<{ diff --git a/src/utils.ts b/src/utils.ts index 991bb0c4..738872e4 100644 --- a/src/utils.ts +++ b/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 }; };