From 76d7e6c00ec11a72adfcb10fbfad6d92a873b253 Mon Sep 17 00:00:00 2001 From: sanine Date: Sun, 29 May 2022 20:09:42 -0500 Subject: add centered zooming --- src/Canvas.js | 60 ++++++++++++++++++++++++++++++++++++++++++----------------- src/main.js | 6 +++--- 2 files changed, 46 insertions(+), 20 deletions(-) diff --git a/src/Canvas.js b/src/Canvas.js index bd39f47..7f6404b 100644 --- a/src/Canvas.js +++ b/src/Canvas.js @@ -12,7 +12,10 @@ class Canvas { this.zoom = 1; const ZOOM_SPEED = 1.2; - this.mousePos = { x: 0, y: 0 }; + this.mouse = { + screenPos: { x: 0, y: 0 }, + drawingPos: { x: 0, y: 0 }, + }; this.pan = { x: 0, y: 0 }; this.panning = false; @@ -24,14 +27,11 @@ class Canvas { /* mouse movement */ this.element.addEventListener('mousemove', e => { - const matrix = this.context.getTransform(); - matrix.invertSelf(); - const screenX = e.offsetX; const screenY = e.offsetY; - - /* compute drawing-space position */ - const x = matrix.a*screenX + matrix.b*screenY + matrix.e; - const y = matrix.c*screenX + matrix.d*screenY + matrix.f; + this.mouse.screenPos.x = e.offsetX; + this.mouse.screenPos.y = e.offsetY; + const [xd, yd] = this.screenToDrawingPos(e.offsetX, e.offsetY); + /* compute movement */ const dx = e.movementX / (this.zoom * this.scale); const dy = e.movementY / (this.zoom * this.scale); @@ -43,14 +43,15 @@ class Canvas { this.setTransform(); } - this.mousePos.x = x; - this.mousePos.y = y; + this.mouse.drawingPos.x = xd; + this.mouse.drawingPos.y = yd; if (this.onMouseMove) this.onMouseMove(this.mousePos); }); /* clicking */ this.element.addEventListener('mousedown', e => { + e.preventDefault(); if (e.button === 1) this.panning = true; }); this.element.addEventListener('mouseup', e => { @@ -65,9 +66,19 @@ class Canvas { /* mouse wheel */ this.element.addEventListener('wheel', e => { if (this.panning) return; // don't zoom and pan simultaneously + const delta = e.deltaY < 0 ? ZOOM_SPEED : 1/ZOOM_SPEED; + const alpha = (1/delta) - 1; + + /* zoom in */ this.zoom *= delta; - this.zoom = clamp(this.zoom, 1, Infinity); + this.zoom = clamp(this.zoom, 1, 1000); + this.setTransform(); + + /* pan to keep mouse in the same place */ + const [mouseX, mouseY] = this.screenToDrawingPos(this.mouse.screenPos.x, this.mouse.screenPos.y); + this.pan.x += mouseX - this.mouse.drawingPos.x; + this.pan.y += mouseY - this.mouse.drawingPos.y; this.setTransform(); this.draw(); }); @@ -80,14 +91,10 @@ class Canvas { setTransform() { /* clamp pan */ - console.log('---'); - console.log(this.pan); - const xMax = this.element.width / (this.zoom * this.scale) - 1; - const yMax = this.element.height / (this.zoom * this.scale) - 1; + const xMax = this.pixelsToUnits(this.element.width) - 1; + const yMax = this.pixelsToUnits(this.element.height) - 1; this.pan.x = clamp(this.pan.x, xMax, 0); this.pan.y = clamp(this.pan.y, yMax, 0); - console.log(xMax, yMax); - console.log(this.pan); this.context.setTransform(1, 0, 0, 1, 0, 0); this.context.scale(this.zoom * this.scale, this.zoom * this.scale); @@ -105,6 +112,25 @@ class Canvas { this.draw(); } + pixelsToUnits(value) { + return value / this.zoom / this.scale; + } + + unitsToPixels(value) { + return value * this.zoom * this.scale; + } + + screenToDrawingPos(xs, ys) { + const matrix = this.context.getTransform(); + matrix.invertSelf(); + + /* compute drawing-space position */ + const x = matrix.a*xs + matrix.b*ys + matrix.e; + const y = matrix.c*xs + matrix.d*ys + matrix.f; + + return [ x, y ]; + } + draw() { this.context.clearRect(0, 0, 1, 1); if (this.onDraw) this.onDraw(this.context); diff --git a/src/main.js b/src/main.js index 5c9b085..d618d05 100644 --- a/src/main.js +++ b/src/main.js @@ -5,7 +5,7 @@ const $ = id => document.getElementById(id) window.onload = () => { const canvas = new Canvas('root'); - const pos = canvas.mousePos; + const pos = canvas.mouse.drawingPos; canvas.onMouseMove = () => canvas.draw(); canvas.onDraw = ct => { @@ -19,9 +19,9 @@ window.onload = () => { ct.fillRect(2/3, 2/3, 1/3, 1/3); ct.strokeStyle = '#fff'; - ct.lineWidth = 1/(canvas.zoom * canvas.scale); + ct.lineWidth = canvas.pixelsToUnits(1); ct.beginPath(); - ct.arc(pos.x, pos.y, 30 /(canvas.zoom * canvas.scale), 0, 2*Math.PI); + ct.arc(pos.x, pos.y, canvas.pixelsToUnits(30), 0, 2*Math.PI); ct.closePath(); ct.stroke(); }; -- cgit v1.2.1