fix: ensure proper drawing of shape tools after pan/zoom (#3066)

* fix: ensure proper drawing of shape tools after pan/zoom

* remove sticky bottom toolbar

* make bounding-box not selectable after undo/redo

* fix: resize issue, added option to pan using alt/shift + mouse wheel

* drag with touchpad

* use isDesktop and move globalThis to index.ts

* gesture event not required, use preventDefault with passive false in wheel event

* use shift in mac and ctrl in pc
This commit is contained in:
Mani 2024-03-16 15:05:51 +08:00 committed by GitHub
parent 6ff42155ad
commit 5eafd82521
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 78 additions and 50 deletions

View file

@ -6,19 +6,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import * as tr from "@tslib/ftl";
import Container from "../components/Container.svelte";
import { addOrUpdateNote } from "./add-or-update-note";
import type { IOMode } from "./lib";
import MasksEditor from "./MaskEditor.svelte";
import Notes from "./Notes.svelte";
import StickyFooter from "./StickyFooter.svelte";
import { hideAllGuessOne, textEditingState } from "./store";
import { textEditingState } from "./store";
export let mode: IOMode;
async function addNote(): Promise<void> {
addOrUpdateNote(mode, $hideAllGuessOne);
}
const items = [
{ label: tr.notetypesOcclusionMask(), value: 1 },
{ label: tr.notetypesOcclusionNote(), value: 2 },
@ -51,8 +45,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<div hidden={activeTabValue != 2}>
<Notes />
</div>
<StickyFooter {mode} {addNote} />
</Container>
<style lang="scss">

View file

@ -5,6 +5,7 @@ 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";
@ -31,7 +32,7 @@ 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 } from "./tools/tool-zoom";
import { disableZoom, enableZoom, onWheelDrag } from "./tools/tool-zoom";
export let canvas;
export let iconSize;
@ -54,6 +55,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
let dbclicked = false;
let move = false;
let wheel = false;
const controlKey = isApplePlatform() ? "Shift" : "Control";
onMount(() => {
window.addEventListener("mousedown", (event) => {
@ -82,7 +84,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}
});
window.addEventListener("keyup", (event) => {
if (event.key == "Control") {
if (event.key === controlKey) {
clicked = false;
move = false;
wheel = false;
@ -90,34 +92,33 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}
});
window.addEventListener("keydown", (event) => {
if (event.key == "Control") {
clicked = false;
move = false;
wheel = false;
dbclicked = false;
}
});
window.addEventListener("keydown", (event) => {
if (event.key == "Control" && activeTool != "magnify") {
if (event.key === controlKey) {
stopDraw(canvas);
enableZoom(canvas);
}
});
window.addEventListener("keyup", (event) => {
if (event.key == "Control" && activeTool != "magnify") {
if (event.key === controlKey) {
disableFunctions();
handleToolChanges(activeTool);
}
});
window.addEventListener("wheel", () => {
if (clicked && move && wheel && !dbclicked) {
stopDraw(canvas);
enableZoom(canvas);
}
});
window.addEventListener(
"wheel",
(event) => {
event.preventDefault();
if (clicked && move && wheel && !dbclicked) {
stopDraw(canvas);
enableZoom(canvas);
}
onWheelDrag(canvas, event);
},
{ passive: false },
);
});
// handle tool changes after initialization
$: if (canvas) {
const handleToolChanges = (activeTool: string) => {
disableFunctions();
enableSelectable(canvas, true);
// remove unfinished polygon when switching to other tools
@ -127,10 +128,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
case "cursor":
drawCursor(canvas);
break;
case "magnify":
enableZoom(canvas);
enableSelectable(canvas, false);
break;
case "draw-rectangle":
drawRectangle(canvas);
break;
@ -146,6 +143,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
default:
break;
}
};
// handle tool changes after initialization
$: if (canvas) {
handleToolChanges(activeTool);
}
const disableFunctions = () => {

View file

@ -4,10 +4,15 @@
import "./image-occlusion-base.scss";
import { ModuleName, setupI18n } from "@tslib/i18n";
import { get } from "svelte/store";
import { checkNightMode } from "../lib/nightmode";
import { addOrUpdateNote } from "./add-or-update-note";
import ImageOcclusionPage from "./ImageOcclusionPage.svelte";
import type { IOMode } from "./lib";
import { hideAllGuessOne } from "./store";
globalThis.anki = globalThis.anki || {};
const i18n = setupI18n({
modules: [
@ -25,6 +30,16 @@ export async function setupImageOcclusion(mode: IOMode, target = document.body):
checkNightMode();
await i18n;
async function addNote(): Promise<void> {
addOrUpdateNote(mode, get(hideAllGuessOne));
}
// for adding note from mobile devices
globalThis.anki.imageOcclusion = {
mode,
addNote,
};
return new ImageOcclusionPage({
target: target,
props: {

View file

@ -128,7 +128,6 @@ const setupBoundingBox = (canvas: fabric.Canvas, size: Size): fabric.Rect => {
lockMovementY: true,
selectable: false,
evented: false,
stroke: "red",
});
canvas.add(boundingBox);

View file

@ -291,6 +291,8 @@ export const selectAllShapes = (canvas: fabric.Canvas) => {
export const isPointerInBoundingBox = (pointer): boolean => {
const boundingBox = getBoundingBox();
boundingBox.selectable = false;
boundingBox.evented = false;
if (
pointer.x < boundingBox.left
|| pointer.x > boundingBox.left + boundingBox.width

View file

@ -6,7 +6,6 @@ import * as tr from "@tslib/ftl";
import {
mdiCursorDefaultOutline,
mdiEllipseOutline,
mdiMagnifyScan,
mdiRectangleOutline,
mdiTextBox,
mdiVectorPolygonVariant,
@ -14,7 +13,6 @@ import {
import {
cursorKeyCombination,
ellipseKeyCombination,
magnifyKeyCombination,
polygonKeyCombination,
rectangleKeyCombination,
textKeyCombination,
@ -27,12 +25,6 @@ export const tools = [
tooltip: tr.editingImageOcclusionSelectTool,
shortcut: cursorKeyCombination,
},
{
id: "magnify",
icon: mdiMagnifyScan,
tooltip: tr.editingImageOcclusionZoomTool,
shortcut: magnifyKeyCombination,
},
{
id: "draw-rectangle",
icon: mdiRectangleOutline,

View file

@ -4,6 +4,7 @@
// https://codepen.io/amsunny/pen/XWGLxye
// canvas.viewportTransform = [ scaleX, skewX, skewY, scaleY, translateX, translateY ]
import { isDesktop } from "@tslib/platform";
import type { fabric } from "fabric";
import Hammer from "hammerjs";
@ -47,9 +48,15 @@ export const zoomOut = (canvas): void => {
};
export const zoomReset = (canvas: fabric.Canvas): void => {
canvas.zoomToPoint({ x: canvas.width / 2, y: canvas.height / 2 }, 1);
zoomResetInner(canvas);
// reset again to update the viewportTransform
zoomResetInner(canvas);
};
const zoomResetInner = (canvas: fabric.Canvas): void => {
fitCanvasVptScale(canvas);
constrainBoundsAroundBgImage(canvas);
const vpt = canvas.viewportTransform;
canvas.zoomToPoint({ x: canvas.width / 2, y: canvas.height / 2 }, vpt[0]);
};
export const enablePinchZoom = (canvas: fabric.Canvas) => {
@ -74,8 +81,7 @@ export const disablePinchZoom = (canvas: fabric.Canvas) => {
export const onResize = (canvas: fabric.Canvas) => {
setCanvasSize(canvas);
constrainBoundsAroundBgImage(canvas);
fitCanvasVptScale(canvas);
zoomReset(canvas);
};
const onMouseWheel = (opt) => {
@ -151,6 +157,23 @@ const onDrag = (canvas, opt) => {
redraw(canvas);
};
export const onWheelDrag = (canvas: fabric.Canvas, event: WheelEvent) => {
const deltaX = event.deltaX;
const deltaY = event.deltaY;
const vpt = canvas.viewportTransform;
canvas.lastPosX = event.clientX;
canvas.lastPosY = event.clientY;
vpt[4] -= deltaX;
vpt[5] -= deltaY;
canvas.lastPosX -= deltaX;
canvas.lastPosY -= deltaY;
canvas.setViewportTransform(vpt);
constrainBoundsAroundBgImage(canvas);
redraw(canvas);
};
const onMouseUp = () => {
isDragging = false;
const canvas = globalThis.canvas;
@ -176,8 +199,11 @@ export const constrainBoundsAroundBgImage = (canvas: fabric.Canvas) => {
};
export const setCanvasSize = (canvas: fabric.Canvas) => {
canvas.setHeight(window.innerHeight - 76);
canvas.setWidth(window.innerWidth - 39);
const width = window.innerWidth - 39;
let height = window.innerHeight;
height = isDesktop() ? height - 76 : height - 46;
canvas.setHeight(height);
canvas.setWidth(width);
redraw(canvas);
};
@ -205,8 +231,8 @@ const fitCanvasVptScale = (canvas: fabric.Canvas) => {
const getScaleRatio = (boundingBox: fabric.Rect) => {
const h1 = boundingBox.height;
const w1 = boundingBox.width;
const h2 = innerHeight - 79;
const w2 = innerWidth - 42;
let h2 = window.innerHeight;
h2 = isDesktop() ? h2 - 79 : h2 - 48;
return Math.min(w2 / w1, h2 / h1);
};