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

This commit is contained in:
David Luzar 2022-01-29 21:12:44 +01:00 committed by GitHub
parent e4edda4555
commit 79d323fab1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 152 additions and 408 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,30 +1,41 @@
import { NormalizedZoomValue, PointerCoords, Zoom } from "../types"; import { AppState, NormalizedZoomValue } 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),
},
};
};
export const getNormalizedZoom = (zoom: number): NormalizedZoomValue => { export const getNormalizedZoom = (zoom: number): NormalizedZoomValue => {
const normalizedZoom = parseFloat(zoom.toFixed(2)); const normalizedZoom = parseFloat(zoom.toFixed(2));
const clampedZoom = Math.max(0.1, Math.min(normalizedZoom, 30)); const clampedZoom = Math.max(0.1, Math.min(normalizedZoom, 30));
return clampedZoom as NormalizedZoomValue; 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,
},
};
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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