move mouse events to PanZoom
This commit is contained in:
parent
93abdf6bcb
commit
f396b4a16a
|
@ -1,14 +1,8 @@
|
|||
import React, { createRef, useEffect } from "react";
|
||||
import {
|
||||
TransformComponent,
|
||||
TransformWrapper,
|
||||
useControls,
|
||||
useTransformEffect,
|
||||
} from "react-zoom-pan-pinch";
|
||||
import React, { createRef, useContext, useEffect } from "react";
|
||||
import { Canvas } from "../lib/canvas";
|
||||
import { useAppContext } from "../contexts/AppContext";
|
||||
import throttle from "lodash.throttle";
|
||||
import { PanZoomWrapper } from "@sc07-canvas/lib/src/renderer";
|
||||
import { RendererContext } from "@sc07-canvas/lib/src/renderer/RendererContext";
|
||||
|
||||
export const CanvasWrapper = () => {
|
||||
// to prevent safari from blurring things, use the zoom css property
|
||||
|
@ -24,6 +18,7 @@ export const CanvasWrapper = () => {
|
|||
const CanvasInner = () => {
|
||||
const canvasRef = createRef<HTMLCanvasElement>();
|
||||
const { config } = useAppContext();
|
||||
const PanZoom = useContext(RendererContext);
|
||||
// const { centerView } = useControls();
|
||||
|
||||
// useTransformEffect(
|
||||
|
@ -39,7 +34,7 @@ const CanvasInner = () => {
|
|||
useEffect(() => {
|
||||
if (!config.canvas || !canvasRef.current) return;
|
||||
const canvas = canvasRef.current!;
|
||||
const canvasInstance = new Canvas(config, canvas);
|
||||
const canvasInstance = new Canvas(config, canvas, PanZoom);
|
||||
// centerView();
|
||||
|
||||
return () => {
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
import EventEmitter from "eventemitter3";
|
||||
import { ClientConfig, IPalleteContext, Pixel } from "../types";
|
||||
import Network from "./network";
|
||||
import {
|
||||
ClickEvent,
|
||||
HoverEvent,
|
||||
PanZoom,
|
||||
} from "@sc07-canvas/lib/src/renderer/PanZoom";
|
||||
|
||||
export class Canvas extends EventEmitter {
|
||||
static instance: Canvas | undefined;
|
||||
|
@ -8,6 +13,7 @@ export class Canvas extends EventEmitter {
|
|||
private _destroy = false;
|
||||
private config: ClientConfig;
|
||||
private canvas: HTMLCanvasElement;
|
||||
private PanZoom: PanZoom;
|
||||
private ctx: CanvasRenderingContext2D;
|
||||
|
||||
private cursor = { x: -1, y: -1 };
|
||||
|
@ -16,24 +22,27 @@ export class Canvas extends EventEmitter {
|
|||
} = {};
|
||||
private lastPlace: number | undefined;
|
||||
|
||||
constructor(config: ClientConfig, canvas: HTMLCanvasElement) {
|
||||
constructor(
|
||||
config: ClientConfig,
|
||||
canvas: HTMLCanvasElement,
|
||||
PanZoom: PanZoom
|
||||
) {
|
||||
super();
|
||||
Canvas.instance = this;
|
||||
|
||||
this.config = config;
|
||||
this.canvas = canvas;
|
||||
this.PanZoom = PanZoom;
|
||||
this.ctx = canvas.getContext("2d")!;
|
||||
|
||||
canvas.width = config.canvas.size[0];
|
||||
canvas.height = config.canvas.size[1];
|
||||
|
||||
canvas.addEventListener("mousemove", this.handleMouseMove.bind(this));
|
||||
canvas.addEventListener("mouseup", this.handleMouseClick.bind(this));
|
||||
canvas.addEventListener("mousedown", this.handleMouseDown.bind(this));
|
||||
this.PanZoom.addListener("hover", this.handleMouseMove.bind(this));
|
||||
this.PanZoom.addListener("click", this.handleMouseDown.bind(this));
|
||||
|
||||
this.on("pallete", this.updatePallete.bind(this));
|
||||
|
||||
// Network.on("canvas", this.handleBatch.bind(this));
|
||||
Network.waitFor("canvas").then(([pixels]) => this.handleBatch(pixels));
|
||||
|
||||
this.draw();
|
||||
|
@ -42,53 +51,24 @@ export class Canvas extends EventEmitter {
|
|||
destroy() {
|
||||
this._destroy = true;
|
||||
|
||||
this.canvas.removeEventListener(
|
||||
"mousemove",
|
||||
this.handleMouseMove.bind(this)
|
||||
);
|
||||
this.canvas.removeEventListener(
|
||||
"mouseup",
|
||||
this.handleMouseClick.bind(this)
|
||||
);
|
||||
this.canvas.removeEventListener(
|
||||
"mousedown",
|
||||
this.handleMouseDown.bind(this)
|
||||
);
|
||||
this.PanZoom.removeListener("hover", this.handleMouseMove.bind(this));
|
||||
this.PanZoom.removeListener("click", this.handleMouseDown.bind(this));
|
||||
|
||||
Network.off("canvas", this.handleBatch.bind(this));
|
||||
}
|
||||
|
||||
private downTime: number | undefined;
|
||||
private dragOrigin: { x: number; y: number } = { x: 0, y: 0 };
|
||||
|
||||
handleMouseClick(e: MouseEvent) {
|
||||
const downDelta = Date.now() - this.downTime!;
|
||||
const delta = [
|
||||
Math.abs(this.dragOrigin.x - e.clientX),
|
||||
Math.abs(this.dragOrigin.y - e.clientY),
|
||||
];
|
||||
if (downDelta < 500) {
|
||||
// mouse was down for less than 500ms
|
||||
|
||||
if (delta[0] < 5 && delta[1] < 5) {
|
||||
const [x, y] = this.screenToPos(e.clientX, e.clientY);
|
||||
this.place(x, y);
|
||||
}
|
||||
}
|
||||
handleMouseDown(e: ClickEvent) {
|
||||
const [x, y] = this.screenToPos(e.clientX, e.clientY);
|
||||
this.place(x, y);
|
||||
}
|
||||
|
||||
handleMouseDown(e: MouseEvent) {
|
||||
this.downTime = Date.now();
|
||||
this.dragOrigin = { x: e.pageX, y: e.pageY };
|
||||
}
|
||||
|
||||
handleMouseMove(e: MouseEvent) {
|
||||
handleMouseMove(e: HoverEvent) {
|
||||
const canvasRect = this.canvas.getBoundingClientRect();
|
||||
if (
|
||||
canvasRect.left <= e.pageX &&
|
||||
canvasRect.right >= e.pageX &&
|
||||
canvasRect.top <= e.pageY &&
|
||||
canvasRect.bottom >= e.pageY
|
||||
canvasRect.left <= e.clientX &&
|
||||
canvasRect.right >= e.clientX &&
|
||||
canvasRect.top <= e.clientY &&
|
||||
canvasRect.bottom >= e.clientY
|
||||
) {
|
||||
const [x, y] = this.screenToPos(e.clientX, e.clientY);
|
||||
this.cursor.x = x;
|
||||
|
@ -101,8 +81,8 @@ export class Canvas extends EventEmitter {
|
|||
|
||||
handleBatch(pixels: string[]) {
|
||||
pixels.forEach((hex, index) => {
|
||||
const x = index / this.config.canvas.size[0];
|
||||
const y = index % this.config.canvas.size[0];
|
||||
const x = index % this.config.canvas.size[0];
|
||||
const y = index / this.config.canvas.size[1];
|
||||
const color = this.Pallete.getColorFromHex(hex);
|
||||
|
||||
this.pixels[x + "_" + y] = {
|
||||
|
@ -169,14 +149,35 @@ export class Canvas extends EventEmitter {
|
|||
}
|
||||
|
||||
screenToPos(x: number, y: number) {
|
||||
// the rendered dimentions in the browser
|
||||
const rect = this.canvas.getBoundingClientRect();
|
||||
const scale = [
|
||||
this.canvas.width / rect.width,
|
||||
this.canvas.height / rect.height,
|
||||
];
|
||||
return [x - rect.left, y - rect.top]
|
||||
.map((v, i) => v * scale[i])
|
||||
.map((v) => v >> 0);
|
||||
|
||||
let output = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
};
|
||||
|
||||
if (this.PanZoom.flags.useZoom) {
|
||||
const scale = this.PanZoom.transform.scale;
|
||||
|
||||
output.x = x / scale - rect.left;
|
||||
output.y = y / scale - rect.top;
|
||||
} else {
|
||||
// get the ratio
|
||||
const scale = [
|
||||
this.canvas.width / rect.width,
|
||||
this.canvas.height / rect.height,
|
||||
];
|
||||
|
||||
output.x = (x - rect.left) * scale[0];
|
||||
output.y = (y - rect.top) * scale[1];
|
||||
}
|
||||
|
||||
// floor it, we're getting canvas coords, which can't have decimals
|
||||
output.x >>= 0;
|
||||
output.y >>= 0;
|
||||
|
||||
return [output.x, output.y];
|
||||
}
|
||||
|
||||
draw() {
|
||||
|
|
|
@ -11,31 +11,94 @@ import {
|
|||
import { Panning } from "./lib/panning.utils";
|
||||
|
||||
interface TransformState {
|
||||
/**
|
||||
* Zoom scale
|
||||
*
|
||||
* < 0 : zoomed out
|
||||
* > 0 : zoomed in
|
||||
*/
|
||||
scale: number;
|
||||
|
||||
/**
|
||||
* X position of canvas
|
||||
*/
|
||||
x: number;
|
||||
|
||||
/**
|
||||
* Y position of canvas
|
||||
*/
|
||||
y: number;
|
||||
}
|
||||
|
||||
interface Flags {
|
||||
/**
|
||||
* If CSS Zoom is used
|
||||
*
|
||||
* CSS Zoom is not supported on Firefox, as it's not a standard
|
||||
* But on iOS, <canvas> is fuzzy (ignoring other css rules) when transform: scale()'d up
|
||||
*
|
||||
* @see https://caniuse.com/css-zoom
|
||||
*/
|
||||
useZoom: boolean;
|
||||
}
|
||||
|
||||
interface TouchState {
|
||||
/**
|
||||
* Timestamp of last touch
|
||||
*/
|
||||
lastTouch: number | null;
|
||||
|
||||
/**
|
||||
* Distance between each finger when pinch starts
|
||||
*/
|
||||
pinchStartDistance: number | null;
|
||||
|
||||
/**
|
||||
* previous distance between each finger
|
||||
*/
|
||||
lastDistance: number | null;
|
||||
|
||||
/**
|
||||
* scale when pinch starts
|
||||
*/
|
||||
pinchStartScale: number | null;
|
||||
|
||||
/**
|
||||
* middle coord of pinch
|
||||
*/
|
||||
pinchMidpoint: { x: number; y: number } | null;
|
||||
}
|
||||
|
||||
interface MouseState {}
|
||||
interface MouseState {
|
||||
/**
|
||||
* timestamp of mouse down
|
||||
*/
|
||||
mouseDown: number | null;
|
||||
}
|
||||
|
||||
interface ISetup {
|
||||
/**
|
||||
* Scale limits
|
||||
* [minimum scale, maximum scale]
|
||||
*/
|
||||
scale: [number, number];
|
||||
}
|
||||
|
||||
// TODO: move these event interfaces out
|
||||
export interface ClickEvent {
|
||||
clientX: number;
|
||||
clientY: number;
|
||||
}
|
||||
|
||||
export interface HoverEvent {
|
||||
clientX: number;
|
||||
clientY: number;
|
||||
}
|
||||
|
||||
interface PanZoomEvents {
|
||||
doubleTap: (e: TouchEvent) => void;
|
||||
click: (e: ClickEvent) => void;
|
||||
hover: (e: HoverEvent) => void;
|
||||
}
|
||||
|
||||
export class PanZoom extends EventEmitter<PanZoomEvents> {
|
||||
|
@ -68,7 +131,9 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
|
|||
pinchMidpoint: null,
|
||||
};
|
||||
|
||||
this.mouse = {};
|
||||
this.mouse = {
|
||||
mouseDown: null,
|
||||
};
|
||||
|
||||
this.panning = new Panning(this);
|
||||
|
||||
|
@ -318,6 +383,8 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
|
|||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
this.mouse.mouseDown = Date.now();
|
||||
|
||||
this.panning.start(e.clientX, e.clientY);
|
||||
},
|
||||
{ passive: false }
|
||||
|
@ -327,12 +394,18 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
|
|||
document.addEventListener(
|
||||
"mousemove",
|
||||
(e) => {
|
||||
if (!this.panning.enabled) return;
|
||||
if (this.panning.enabled) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
this.panning.move(e.clientX, e.clientY);
|
||||
this.panning.move(e.clientX, e.clientY);
|
||||
} else {
|
||||
// not panning
|
||||
this.emit("hover", {
|
||||
clientX: e.clientX,
|
||||
clientY: e.clientY,
|
||||
});
|
||||
}
|
||||
},
|
||||
{ passive: false }
|
||||
);
|
||||
|
@ -341,12 +414,32 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
|
|||
document.addEventListener(
|
||||
"mouseup",
|
||||
(e) => {
|
||||
if (!this.panning.enabled) return;
|
||||
if (this.mouse.mouseDown && Date.now() - this.mouse.mouseDown <= 500) {
|
||||
// if the mouse was down for less than a half a second, it's a click
|
||||
// this can't depend on this.panning.enabled because that'll always be true when mouse is down
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const delta = [
|
||||
Math.abs(this.panning.x - e.clientX),
|
||||
Math.abs(this.panning.y - e.clientY),
|
||||
];
|
||||
|
||||
this.panning.end(e.clientX, e.clientY);
|
||||
if (delta[0] < 5 && delta[1] < 5) {
|
||||
// difference from the start position to the up position is very very slow,
|
||||
// so it's most likely intended to be a click
|
||||
this.emit("click", {
|
||||
clientX: e.clientX,
|
||||
clientY: e.clientY,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (this.panning.enabled) {
|
||||
// currently panning
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
this.panning.end(e.clientX, e.clientY);
|
||||
}
|
||||
},
|
||||
{ passive: false }
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue