better pan zoom with space for drag, shift for horizontal scroll and ctrl for zoom (#3080)

* more better pan zoom with space for drag, shift for horizontal scroll and ctrl for zoom

* use middle mouse button for drag

* improve panning with space + mousemove, remove middle mouse implementation

* handle tool change after mouseup event
This commit is contained in:
Mani 2024-03-18 22:26:41 +08:00 committed by GitHub
parent 58b2475f42
commit f413274fba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 110 additions and 60 deletions

View file

@ -5,7 +5,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<script lang="ts"> <script lang="ts">
import { directionKey } from "@tslib/context-keys"; import { directionKey } from "@tslib/context-keys";
import * as tr from "@tslib/ftl"; import * as tr from "@tslib/ftl";
import { isApplePlatform } from "@tslib/platform";
import { getPlatformString } from "@tslib/shortcuts"; import { getPlatformString } from "@tslib/shortcuts";
import DropdownItem from "components/DropdownItem.svelte"; import DropdownItem from "components/DropdownItem.svelte";
import IconButton from "components/IconButton.svelte"; import IconButton from "components/IconButton.svelte";
@ -32,7 +31,14 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { drawCursor } from "./tools/tool-cursor"; import { drawCursor } from "./tools/tool-cursor";
import { removeUnfinishedPolygon } from "./tools/tool-polygon"; import { removeUnfinishedPolygon } from "./tools/tool-polygon";
import { undoRedoTools, undoStack } from "./tools/tool-undo-redo"; import { undoRedoTools, undoStack } from "./tools/tool-undo-redo";
import { disableZoom, enableZoom, onWheelDrag } from "./tools/tool-zoom"; import {
disablePan,
disableZoom,
enablePan,
enableZoom,
onWheelDrag,
onWheelDragX,
} from "./tools/tool-zoom";
export let canvas; export let canvas;
export let iconSize; export let iconSize;
@ -51,56 +57,70 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}); });
// handle zoom event when mouse scroll and ctrl key are hold for panzoom // handle zoom event when mouse scroll and ctrl key are hold for panzoom
let clicked = false; let spaceClicked = false;
let dbclicked = false; let controlClicked = false;
let shiftClicked = false;
let move = false; let move = false;
let wheel = false; const spaceKey = " ";
const controlKey = isApplePlatform() ? "Shift" : "Control"; const controlKey = "Control";
const shiftKey = "Shift";
onMount(() => { onMount(() => {
window.addEventListener("mousedown", (event) => { window.addEventListener("mousedown", () => {
if (event.ctrlKey) { window.addEventListener("keydown", (ev) => {
clicked = true; if (ev.key === spaceKey) {
spaceClicked = true;
} }
}); });
window.addEventListener("mouseup", (event) => { });
if (event.ctrlKey) { window.addEventListener("mousemove", () => {
clicked = false; if (spaceClicked || move) {
disableFunctions();
enablePan(canvas);
} }
}); });
window.addEventListener("mousemove", (event) => { window.addEventListener("mouseup", () => {
if (event.ctrlKey) { if (spaceClicked) {
move = true; spaceClicked = false;
}
if (move) {
move = false;
}
disableFunctions();
handleToolChanges(activeTool);
});
window.addEventListener("keyup", (event) => {
if (
event.key === spaceKey ||
event.key === controlKey ||
event.key === shiftKey
) {
spaceClicked = false;
controlClicked = false;
shiftClicked = false;
move = false;
disableFunctions();
handleToolChanges(activeTool);
}
});
window.addEventListener("keydown", (event) => {
if (event.key === spaceKey) {
spaceClicked = true;
}
if (event.key === controlKey) {
controlClicked = true;
}
if (event.key === shiftKey) {
shiftClicked = true;
} }
}); });
window.addEventListener("wheel", (event) => { window.addEventListener("wheel", (event) => {
if (event.ctrlKey) { if (event.ctrlKey) {
wheel = true; controlClicked = true;
} }
}); if (event.shiftKey) {
window.addEventListener("dblclick", (event) => { shiftClicked = true;
if (event.ctrlKey) {
dbclicked = true;
}
});
window.addEventListener("keyup", (event) => {
if (event.key === controlKey) {
clicked = false;
move = false;
wheel = false;
dbclicked = false;
}
});
window.addEventListener("keydown", (event) => {
if (event.key === controlKey) {
stopDraw(canvas);
enableZoom(canvas);
}
});
window.addEventListener("keyup", (event) => {
if (event.key === controlKey) {
disableFunctions();
handleToolChanges(activeTool);
} }
}); });
window.addEventListener( window.addEventListener(
@ -108,10 +128,17 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
(event) => { (event) => {
event.preventDefault(); event.preventDefault();
if (clicked && move && wheel && !dbclicked) { if (controlClicked) {
stopDraw(canvas); disableFunctions();
enableZoom(canvas); enableZoom(canvas);
return;
} }
if (shiftClicked) {
onWheelDragX(canvas, event);
return;
}
onWheelDrag(canvas, event); onWheelDrag(canvas, event);
}, },
{ passive: false }, { passive: false },
@ -153,6 +180,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
const disableFunctions = () => { const disableFunctions = () => {
stopDraw(canvas); stopDraw(canvas);
disableZoom(canvas); disableZoom(canvas);
disablePan(canvas);
}; };
function changeOcclusionType(occlusionType: "all" | "one"): void { function changeOcclusionType(occlusionType: "all" | "one"): void {

View file

@ -10,8 +10,6 @@ import Hammer from "hammerjs";
import { getBoundingBox, redraw } from "./lib"; import { getBoundingBox, redraw } from "./lib";
let isDragging = false;
const minScale = 0.5; const minScale = 0.5;
const maxScale = 5; const maxScale = 5;
let zoomScale = 1; let zoomScale = 1;
@ -19,6 +17,9 @@ export let currentScale = 1;
export const enableZoom = (canvas: fabric.Canvas) => { export const enableZoom = (canvas: fabric.Canvas) => {
canvas.on("mouse:wheel", onMouseWheel); canvas.on("mouse:wheel", onMouseWheel);
};
export const enablePan = (canvas: fabric.Canvas) => {
canvas.on("mouse:down", onMouseDown); canvas.on("mouse:down", onMouseDown);
canvas.on("mouse:move", onMouseMove); canvas.on("mouse:move", onMouseMove);
canvas.on("mouse:up", onMouseUp); canvas.on("mouse:up", onMouseUp);
@ -26,6 +27,9 @@ export const enableZoom = (canvas: fabric.Canvas) => {
export const disableZoom = (canvas: fabric.Canvas) => { export const disableZoom = (canvas: fabric.Canvas) => {
canvas.off("mouse:wheel", onMouseWheel); canvas.off("mouse:wheel", onMouseWheel);
};
export const disablePan = (canvas: fabric.Canvas) => {
canvas.off("mouse:down", onMouseDown); canvas.off("mouse:down", onMouseDown);
canvas.off("mouse:move", onMouseMove); canvas.off("mouse:move", onMouseMove);
canvas.off("mouse:up", onMouseUp); canvas.off("mouse:up", onMouseUp);
@ -98,7 +102,6 @@ const onMouseWheel = (opt) => {
}; };
const onMouseDown = (opt) => { const onMouseDown = (opt) => {
isDragging = true;
const canvas = globalThis.canvas; const canvas = globalThis.canvas;
canvas.discardActiveObject(); canvas.discardActiveObject();
const { e } = opt; const { e } = opt;
@ -111,7 +114,6 @@ const onMouseDown = (opt) => {
export const onMouseMove = (opt) => { export const onMouseMove = (opt) => {
const canvas = globalThis.canvas; const canvas = globalThis.canvas;
if (isDragging) {
canvas.discardActiveObject(); canvas.discardActiveObject();
if (!canvas.viewportTransform) { if (!canvas.viewportTransform) {
return; return;
@ -123,7 +125,6 @@ export const onMouseMove = (opt) => {
} }
onDrag(canvas, opt); onDrag(canvas, opt);
}
}; };
// initializes lastPosX and lastPosY because it is undefined in touchmove event // initializes lastPosX and lastPosY because it is undefined in touchmove event
@ -133,6 +134,17 @@ document.addEventListener("touchstart", (e) => {
canvas.lastPosY = e.touches[0].clientY; canvas.lastPosY = e.touches[0].clientY;
}); });
// initializes lastPosX and lastPosY because it is undefined before mousemove event
document.addEventListener("mousemove", (event) => {
document.addEventListener("keydown", (e) => {
if (e.key === " ") {
const canvas = globalThis.canvas;
canvas.lastPosX = event.clientX;
canvas.lastPosY = event.clientY;
}
});
});
export const onPinchZoom = (opt): boolean => { export const onPinchZoom = (opt): boolean => {
const { e } = opt; const { e } = opt;
const canvas = globalThis.canvas; const canvas = globalThis.canvas;
@ -151,8 +163,8 @@ const onDrag = (canvas, opt) => {
vpt[4] += clientX - canvas.lastPosX; vpt[4] += clientX - canvas.lastPosX;
vpt[5] += clientY - canvas.lastPosY; vpt[5] += clientY - canvas.lastPosY;
canvas.lastPosX = clientX; canvas.lastPosX += clientX - canvas.lastPosX;
canvas.lastPosY = clientY; canvas.lastPosY += clientY - canvas.lastPosY;
constrainBoundsAroundBgImage(canvas); constrainBoundsAroundBgImage(canvas);
redraw(canvas); redraw(canvas);
}; };
@ -174,8 +186,18 @@ export const onWheelDrag = (canvas: fabric.Canvas, event: WheelEvent) => {
redraw(canvas); redraw(canvas);
}; };
export const onWheelDragX = (canvas: fabric.Canvas, event: WheelEvent) => {
const delta = event.deltaY;
const vpt = canvas.viewportTransform;
canvas.lastPosY = event.clientY;
vpt[4] -= delta;
canvas.lastPosX -= delta;
canvas.setViewportTransform(vpt);
constrainBoundsAroundBgImage(canvas);
redraw(canvas);
};
const onMouseUp = () => { const onMouseUp = () => {
isDragging = false;
const canvas = globalThis.canvas; const canvas = globalThis.canvas;
canvas.setViewportTransform(canvas.viewportTransform); canvas.setViewportTransform(canvas.viewportTransform);
constrainBoundsAroundBgImage(canvas); constrainBoundsAroundBgImage(canvas);