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">
import { directionKey } from "@tslib/context-keys";
import * as tr from "@tslib/ftl";
import { isApplePlatform } from "@tslib/platform";
import { getPlatformString } from "@tslib/shortcuts";
import DropdownItem from "components/DropdownItem.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 { removeUnfinishedPolygon } from "./tools/tool-polygon";
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 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
let clicked = false;
let dbclicked = false;
let spaceClicked = false;
let controlClicked = false;
let shiftClicked = false;
let move = false;
let wheel = false;
const controlKey = isApplePlatform() ? "Shift" : "Control";
const spaceKey = " ";
const controlKey = "Control";
const shiftKey = "Shift";
onMount(() => {
window.addEventListener("mousedown", (event) => {
if (event.ctrlKey) {
clicked = true;
window.addEventListener("mousedown", () => {
window.addEventListener("keydown", (ev) => {
if (ev.key === spaceKey) {
spaceClicked = true;
}
});
});
window.addEventListener("mousemove", () => {
if (spaceClicked || move) {
disableFunctions();
enablePan(canvas);
}
});
window.addEventListener("mouseup", (event) => {
if (event.ctrlKey) {
clicked = false;
window.addEventListener("mouseup", () => {
if (spaceClicked) {
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("mousemove", (event) => {
if (event.ctrlKey) {
move = true;
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) => {
if (event.ctrlKey) {
wheel = true;
controlClicked = true;
}
});
window.addEventListener("dblclick", (event) => {
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);
if (event.shiftKey) {
shiftClicked = true;
}
});
window.addEventListener(
@ -108,10 +128,17 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
(event) => {
event.preventDefault();
if (clicked && move && wheel && !dbclicked) {
stopDraw(canvas);
if (controlClicked) {
disableFunctions();
enableZoom(canvas);
return;
}
if (shiftClicked) {
onWheelDragX(canvas, event);
return;
}
onWheelDrag(canvas, event);
},
{ passive: false },
@ -153,6 +180,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
const disableFunctions = () => {
stopDraw(canvas);
disableZoom(canvas);
disablePan(canvas);
};
function changeOcclusionType(occlusionType: "all" | "one"): void {

View file

@ -10,8 +10,6 @@ import Hammer from "hammerjs";
import { getBoundingBox, redraw } from "./lib";
let isDragging = false;
const minScale = 0.5;
const maxScale = 5;
let zoomScale = 1;
@ -19,6 +17,9 @@ export let currentScale = 1;
export const enableZoom = (canvas: fabric.Canvas) => {
canvas.on("mouse:wheel", onMouseWheel);
};
export const enablePan = (canvas: fabric.Canvas) => {
canvas.on("mouse:down", onMouseDown);
canvas.on("mouse:move", onMouseMove);
canvas.on("mouse:up", onMouseUp);
@ -26,6 +27,9 @@ export const enableZoom = (canvas: fabric.Canvas) => {
export const disableZoom = (canvas: fabric.Canvas) => {
canvas.off("mouse:wheel", onMouseWheel);
};
export const disablePan = (canvas: fabric.Canvas) => {
canvas.off("mouse:down", onMouseDown);
canvas.off("mouse:move", onMouseMove);
canvas.off("mouse:up", onMouseUp);
@ -98,7 +102,6 @@ const onMouseWheel = (opt) => {
};
const onMouseDown = (opt) => {
isDragging = true;
const canvas = globalThis.canvas;
canvas.discardActiveObject();
const { e } = opt;
@ -111,19 +114,17 @@ const onMouseDown = (opt) => {
export const onMouseMove = (opt) => {
const canvas = globalThis.canvas;
if (isDragging) {
canvas.discardActiveObject();
if (!canvas.viewportTransform) {
return;
}
// handle pinch zoom and pan for mobile devices
if (onPinchZoom(opt)) {
return;
}
onDrag(canvas, opt);
canvas.discardActiveObject();
if (!canvas.viewportTransform) {
return;
}
// handle pinch zoom and pan for mobile devices
if (onPinchZoom(opt)) {
return;
}
onDrag(canvas, opt);
};
// 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;
});
// 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 => {
const { e } = opt;
const canvas = globalThis.canvas;
@ -151,8 +163,8 @@ const onDrag = (canvas, opt) => {
vpt[4] += clientX - canvas.lastPosX;
vpt[5] += clientY - canvas.lastPosY;
canvas.lastPosX = clientX;
canvas.lastPosY = clientY;
canvas.lastPosX += clientX - canvas.lastPosX;
canvas.lastPosY += clientY - canvas.lastPosY;
constrainBoundsAroundBgImage(canvas);
redraw(canvas);
};
@ -174,8 +186,18 @@ export const onWheelDrag = (canvas: fabric.Canvas, event: WheelEvent) => {
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 = () => {
isDragging = false;
const canvas = globalThis.canvas;
canvas.setViewportTransform(canvas.viewportTransform);
constrainBoundsAroundBgImage(canvas);