Allow canvas shapes to be dragged and zoomed
by Rajesh Danabal
HTML
<canvas id="canvas" width="800" height="600"></canvas>
CSS
body {
margin: 0;
overflow: hidden;
}
canvas {
border: 1px solid #ccc;
display: block;
}
JavaScript
class InteractionManager {
constructor(canvas, context, shapes) {
this.canvas = canvas;
this.ctx = context;
this.shapes = shapes;
this.plugins = { shape: [], container: [] };
this.state = {
draggingShape: null,
draggingCanvas: false,
scale: 1,
offsetX: 0,
offsetY: 0,
lastX: 0,
lastY: 0,
};
this._bindEvents();
}
// Convert screen to world coordinates
toWorld(x, y) {
return {
x: (x - this.state.offsetX) / this.state.scale,
y: (y - this.state.offsetY) / this.state.scale,
};
}
_bindEvents() {
const getMouse = (e) => ({
x: e.offsetX,
y: e.offsetY
});
this.canvas.addEventListener("mousedown", (e) => {
const { x, y } = getMouse(e);
const world = this.toWorld(x, y);
this.state.lastX = x;
this.state.lastY = y;
const target = this.getTarget(world.x, world.y);
if (e.button === 0) {
if (target) {
this.state.draggingShape = target;
this._dispatch("shape", "dragstart", e, target);
} else {
this.state.draggingCanvas = true;
this._dispatch("container", "dragstart", e, null);
}
}
});
this.canvas.addEventListener("mousemove", (e) => {
const { x, y } = getMouse(e);
const dx = x - this.state.lastX;
const dy = y - this.state.lastY;
if (this.state.draggingShape) {
this.state.draggingShape.x += dx / this.state.scale;
this.state.draggingShape.y += dy / this.state.scale;
this._dispatch("shape", "drag", e, this.state.draggingShape);
} else if (this.state.draggingCanvas) {
this.state.offsetX += dx;
this.state.offsetY += dy;
this._dispatch("container", "drag", e, null);
}
this.state.lastX = x;
this.state.lastY = y;
requestAnimationFrame(draw);
});
this.canvas.addEventListener("mouseup", (e) => {
if (this.state.draggingShape) {
...