typescript - HTML Canvas: El elemento dibujado sigue haciendo zoom o desplazándose
No estoy recibiendo ningún error, pero cuando presiono el ratón y muevo el ratón, el objeto en el lienzo se mueve en la dirección correcta pero no se detiene, simplemente se desliza fuera del lienzo, lo mismo sucede cuando hago zoom con la rueda del ratón, hace zoom pero de nuevo no se detiene.
Espero que el elemento en el lienzo deje de hacer zoom cuando deje de mover la rueda del ratón, también espero que el elemento deje de desplazarse cuando suelte el ratón después de arrastrarlo.
CodeSanbox: https://codesandbox.io/s/react-typescript-sdx09
El componente de React que carga el lienzo es
import React, { useEffect, useRef } from 'react';
import { Grid } from './grid';
const App: React.FC = () => {
const canvasRef = useRef<HTMLCanvasElement>(null);
const setGrid = () => {
const grid = new Grid({w: 800, h: 800});
if (canvasRef.current) {
const canvas = canvasRef.current;
const ctx = canvas.getContext('2d');
if (ctx) {
let now = 0;
const tickLoop = (epoch: number) => {
const tick = epoch - now;
now = epoch;
ctx.clearRect(0, 0, 800, 800);
grid.update(tick);
grid.draw(ctx);
requestAnimationFrame(tickLoop);
};
requestAnimationFrame(tickLoop);
}
}
};
useEffect(() => {
setGrid();
});
return (
<React.Fragment>
<canvas
ref={canvasRef}
width={800}
height={800}
></canvas>
</React.Fragment>
);
};
export { App };
El componente importado llamado grid...
import { InputHandler } from './inputHandler';
export interface WHInterface {
w: number;
h: number;
}
export interface ControlInterface {
view: { x: number; y: number; zoom: number };
viewPos: { prevX: number | null; prevY: number | null; isDragging: boolean };
}
export interface InputHandlerInterface {
setZoom(event: WheelEvent): void;
setPan(event: MouseEvent, mouseDown: boolean): void;
setMouseDown(event: MouseEvent, mouseDown: boolean): void;
}
class Grid implements InputHandlerInterface {
canvasSize: WHInterface;
controls: ControlInterface;
constructor(canvasSize: WHInterface) {
this.canvasSize = canvasSize;
this.controls = {
view: { x: 0, y: 0, zoom: 1 },
viewPos: { prevX: null, prevY: null, isDragging: false },
};
new InputHandler(this);
}
update(tick: number) {
//
}
draw(ctx: CanvasRenderingContext2D) {
ctx.fillStyle = 'grey';
ctx.fillRect(0, 0, 800, 800);
ctx.translate(this.controls.view.x, this.controls.view.y);
ctx.scale(this.controls.view.zoom, this.controls.view.zoom);
ctx.fillStyle = 'white';
ctx.fillRect(20, 20, 20, 20);
}
setMouseDown(event: MouseEvent, mouseDown: boolean) {
const { x, y } = event;
if (mouseDown) {
this.controls.viewPos.isDragging = true;
this.controls.viewPos.prevX = x;
this.controls.viewPos.prevY = y;
}
if (!mouseDown) {
this.controls.viewPos.isDragging = false;
this.controls.viewPos.prevX = null;
this.controls.viewPos.prevY = null;
}
}
setPan(event: MouseEvent, mouseDown: boolean) {
const { x, y } = event;
if (mouseDown) {
const pos = {x, y};
let dx;
let dy;
if (this.controls.viewPos.prevX) dx = x - this.controls.viewPos.prevX;
if (this.controls.viewPos.prevY) dy = y - this.controls.viewPos.prevY;
if (this.controls.viewPos.prevX || this.controls.viewPos.prevY) {
if (dx) this.controls.view.x += dx;
if (dy )this.controls.view.y += dy;
this.controls.viewPos.prevX = pos.x;
this.controls.viewPos.prevY = pos.y;
}
}
}
setZoom(event: WheelEvent) {
const {x, y, deltaY} = event;
const direction = deltaY > 0 ? -1 : 1;
const factor = 0.0005;
const zoom = 1 * direction * factor;
const wx = (x - this.controls.view.x) / (800 * this.controls.view.zoom);
const wy = (y - this.controls.view.y) / (800 * this.controls.view.zoom);
this.controls.view.x -= wx * 800 * zoom;
this.controls.view.y -= wy * 800 * zoom;
this.controls.view.zoom += zoom;
}
}
export { Grid };
Y el manejador de entrada
import { InputHandlerInterface } from './grid';
class InputHandler {
mouseDown: boolean;
constructor(grid: InputHandlerInterface) {
this.mouseDown = false;
window.addEventListener(
'wheel',
(event: WheelEvent) => {
grid.setZoom(event);
},
false
);
window.addEventListener(
'mousedown',
(event) => {
this.mouseDown = true;
grid.setMouseDown(event, true);
},
false
);
window.addEventListener(
'mouseup',
(event) => {
this.mouseDown = false;
grid.setMouseDown(event, false);
},
false
);
window.addEventListener(
'mousemove',
(event) => {
grid.setPan(event, this.mouseDown);
},
false
);
}
}
export { InputHandler };