mirror of
https://github.com/ankitects/anki.git
synced 2025-09-21 15:32:23 -04:00
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:
parent
58b2475f42
commit
f413274fba
2 changed files with 110 additions and 60 deletions
|
@ -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("mousemove", () => {
|
||||||
|
if (spaceClicked || move) {
|
||||||
|
disableFunctions();
|
||||||
|
enablePan(canvas);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
window.addEventListener("mouseup", (event) => {
|
window.addEventListener("mouseup", () => {
|
||||||
if (event.ctrlKey) {
|
if (spaceClicked) {
|
||||||
clicked = false;
|
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) => {
|
window.addEventListener("keydown", (event) => {
|
||||||
if (event.ctrlKey) {
|
if (event.key === spaceKey) {
|
||||||
move = true;
|
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 {
|
||||||
|
|
|
@ -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,19 +114,17 @@ 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle pinch zoom and pan for mobile devices
|
|
||||||
if (onPinchZoom(opt)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
onDrag(canvas, opt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
// 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);
|
||||||
|
|
Loading…
Reference in a new issue