allow templates to be moved via cursor + alt (related #28)
This commit is contained in:
parent
5d2ef8989e
commit
e69f5bf618
|
@ -2,10 +2,12 @@ import { useEffect, useRef } from "react";
|
|||
import { Template as TemplateCl } from "../lib/template";
|
||||
import { useAppContext } from "../contexts/AppContext";
|
||||
import { useTemplateContext } from "../contexts/TemplateContext";
|
||||
import { Canvas } from "../lib/canvas";
|
||||
|
||||
export const Template = () => {
|
||||
const { config } = useAppContext();
|
||||
const { enable, url, width, setWidth, x, y, opacity } = useTemplateContext();
|
||||
const { enable, url, width, setWidth, x, y, opacity, setX, setY } =
|
||||
useTemplateContext();
|
||||
const templateHolder = useRef<HTMLDivElement>(null);
|
||||
const instance = useRef<TemplateCl>();
|
||||
|
||||
|
@ -15,15 +17,71 @@ export const Template = () => {
|
|||
return;
|
||||
}
|
||||
|
||||
const templateHolderRef = templateHolder.current;
|
||||
|
||||
instance.current = new TemplateCl(config!, templateHolder.current);
|
||||
|
||||
instance.current.on("autoDetectWidth", (width) => {
|
||||
console.log("autodetectwidth", width);
|
||||
setWidth(width);
|
||||
});
|
||||
|
||||
let startLocation: { clientX: number; clientY: number } | undefined;
|
||||
let offset: [x: number, y: number] = [0, 0];
|
||||
|
||||
const handleMouseDown = (e: MouseEvent) => {
|
||||
if (!e.altKey) return;
|
||||
|
||||
startLocation = { clientX: e.clientX, clientY: e.clientY };
|
||||
offset = [e.offsetX, e.offsetY];
|
||||
Canvas.instance?.getPanZoom().panning.setEnabled(false);
|
||||
};
|
||||
|
||||
const handleMouseMove = (e: MouseEvent) => {
|
||||
if (!startLocation) return;
|
||||
if (!Canvas.instance) {
|
||||
console.warn(
|
||||
"[Template#handleMouseMove] Canvas.instance is not defined"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const deltaX = e.clientX - startLocation.clientX;
|
||||
const deltaY = e.clientY - startLocation.clientY;
|
||||
const newX = startLocation.clientX + deltaX;
|
||||
const newY = startLocation.clientY + deltaY;
|
||||
|
||||
const [canvasX, canvasY] = Canvas.instance.screenToPos(newX, newY);
|
||||
|
||||
templateHolderRef.style.setProperty("left", canvasX - offset[0] + "px");
|
||||
templateHolderRef.style.setProperty("top", canvasY - offset[1] + "px");
|
||||
};
|
||||
|
||||
const handleMouseUp = (e: MouseEvent) => {
|
||||
startLocation = undefined;
|
||||
Canvas.instance?.getPanZoom().panning.setEnabled(true);
|
||||
|
||||
const x = parseInt(
|
||||
templateHolderRef.style.getPropertyValue("left").replace("px", "") ||
|
||||
"0"
|
||||
);
|
||||
const y = parseInt(
|
||||
templateHolderRef.style.getPropertyValue("top").replace("px", "") || "0"
|
||||
);
|
||||
|
||||
setX(x);
|
||||
setY(y);
|
||||
};
|
||||
|
||||
templateHolder.current.addEventListener("mousedown", handleMouseDown);
|
||||
document.addEventListener("mousemove", handleMouseMove);
|
||||
document.addEventListener("mouseup", handleMouseUp);
|
||||
|
||||
return () => {
|
||||
instance.current?.destroy();
|
||||
|
||||
templateHolderRef?.removeEventListener("mousedown", handleMouseDown);
|
||||
document.removeEventListener("mousemove", handleMouseMove);
|
||||
document.removeEventListener("mouseup", handleMouseUp);
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
|
|
@ -95,6 +95,10 @@ export class Canvas extends EventEmitter<CanvasEvents> {
|
|||
return this.config;
|
||||
}
|
||||
|
||||
getPanZoom() {
|
||||
return this.PanZoom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get nearby pixels
|
||||
* @param x
|
||||
|
@ -325,6 +329,12 @@ export class Canvas extends EventEmitter<CanvasEvents> {
|
|||
document.body.appendChild(el);
|
||||
}
|
||||
|
||||
/**
|
||||
* Screen (clientX, clientY) to Canvas position
|
||||
* @param x
|
||||
* @param y
|
||||
* @returns
|
||||
*/
|
||||
screenToPos(x: number, y: number) {
|
||||
// the rendered dimentions in the browser
|
||||
const rect = this.canvas.getBoundingClientRect();
|
||||
|
|
|
@ -25,6 +25,12 @@ const KEYBINDS = enforceObjectType({
|
|||
},
|
||||
{ key: "LONG_PRESS" },
|
||||
],
|
||||
TEMPLATE_MOVE: [
|
||||
{
|
||||
key: "LCLICK",
|
||||
alt: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
class KeybindManager_ extends EventEmitter<{
|
||||
|
@ -34,6 +40,9 @@ class KeybindManager_ extends EventEmitter<{
|
|||
super();
|
||||
// setup listeners
|
||||
|
||||
document.addEventListener("keydown", this.handleKeydown, {
|
||||
passive: false,
|
||||
});
|
||||
document.addEventListener("keyup", this.handleKeyup);
|
||||
document.addEventListener("click", this.handleClick);
|
||||
}
|
||||
|
@ -43,6 +52,20 @@ class KeybindManager_ extends EventEmitter<{
|
|||
// this is global and doesn't depend on any elements, so this shouldn't need to be called
|
||||
}
|
||||
|
||||
handleKeydown = (e: KeyboardEvent) => {
|
||||
const blacklistedElements = ["INPUT"];
|
||||
|
||||
if (e.target instanceof HTMLElement) {
|
||||
if (blacklistedElements.indexOf(e.target.tagName) > -1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (e.key === "Alt") e.preventDefault();
|
||||
if (e.key === "Control") e.preventDefault();
|
||||
if (e.key === "Shift") e.preventDefault();
|
||||
};
|
||||
|
||||
handleKeyup = (e: KeyboardEvent) => {
|
||||
// discard if in an input element
|
||||
|
||||
|
|
|
@ -343,7 +343,7 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
|
|||
* @param e
|
||||
*/
|
||||
private _touch_touchmove = (event: TouchEvent) => {
|
||||
if (this.panning.enabled && event.touches.length === 1) {
|
||||
if (this.panning.active && event.touches.length === 1) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
|
@ -362,7 +362,7 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
|
|||
* @param e
|
||||
*/
|
||||
private _touch_touchend = (event: TouchEvent) => {
|
||||
if (this.touch.lastTouch && this.panning.enabled) {
|
||||
if (this.touch.lastTouch && this.panning.active) {
|
||||
const touch = event.changedTouches[0];
|
||||
const dx = Math.abs(this.panning.x - touch.clientX);
|
||||
const dy = Math.abs(this.panning.y - touch.clientY);
|
||||
|
@ -372,8 +372,8 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
|
|||
}
|
||||
}
|
||||
|
||||
if (this.panning.enabled) {
|
||||
this.panning.enabled = false;
|
||||
if (this.panning.active) {
|
||||
this.panning.active = false;
|
||||
|
||||
const touch = event.changedTouches[0];
|
||||
|
||||
|
@ -391,7 +391,7 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
|
|||
this.touch.pinchStartDistance = distance;
|
||||
this.touch.lastDistance = distance;
|
||||
this.touch.pinchStartScale = this.transform.scale;
|
||||
this.panning.enabled = false;
|
||||
this.panning.active = false;
|
||||
}
|
||||
|
||||
onPinch(event: TouchEvent) {
|
||||
|
@ -560,7 +560,9 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
|
|||
|
||||
this.mouse.mouseDown = Date.now();
|
||||
|
||||
this.panning.start(e.clientX, e.clientY);
|
||||
if (this.panning.enabled) {
|
||||
this.panning.start(e.clientX, e.clientY);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -570,19 +572,7 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
|
|||
* @param e
|
||||
*/
|
||||
private _mouse_mousemove = (e: MouseEvent) => {
|
||||
if (this.panning.enabled) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
this.panning.move(e.clientX, e.clientY);
|
||||
} else {
|
||||
// not panning
|
||||
this.emit("hover", {
|
||||
clientX: e.clientX,
|
||||
clientY: e.clientY,
|
||||
});
|
||||
}
|
||||
if (this.panning.enabled) {
|
||||
if (this.panning.active) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
|
@ -629,7 +619,7 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
|
|||
}
|
||||
}
|
||||
|
||||
if (this.panning.enabled) {
|
||||
if (this.panning.active) {
|
||||
// currently panning
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
|
|
@ -3,7 +3,8 @@ import { PanZoom } from "../PanZoom";
|
|||
export class Panning {
|
||||
private instance: PanZoom;
|
||||
|
||||
public enabled: boolean = false;
|
||||
public active: boolean = false;
|
||||
public enabled: boolean = true;
|
||||
public x: number = 0;
|
||||
public y: number = 0;
|
||||
|
||||
|
@ -11,13 +12,20 @@ export class Panning {
|
|||
this.instance = instance;
|
||||
}
|
||||
|
||||
public setEnabled(enabled: boolean) {
|
||||
this.enabled = enabled;
|
||||
|
||||
this.active = false;
|
||||
this.instance.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* trigger panning start
|
||||
* @param x clientX
|
||||
* @param y clientY
|
||||
*/
|
||||
public start(x: number, y: number) {
|
||||
this.enabled = true;
|
||||
this.active = true;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
@ -45,7 +53,7 @@ export class Panning {
|
|||
* @param y clientY
|
||||
*/
|
||||
public end(x: number, y: number) {
|
||||
this.enabled = false;
|
||||
this.active = false;
|
||||
|
||||
const deltaX = (x - this.x) / this.instance.transform.scale;
|
||||
const deltaY = (y - this.y) / this.instance.transform.scale;
|
||||
|
|
Loading…
Reference in New Issue