summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2022-05-29 20:09:42 -0500
committersanine <sanine.not@pm.me>2022-05-29 20:09:42 -0500
commit76d7e6c00ec11a72adfcb10fbfad6d92a873b253 (patch)
tree463940067ca37449c72b2853d2abf0152d8ba870
parentbc6f5873f17ae10f42f87825ac64b136cfee3d72 (diff)
add centered zooming
-rw-r--r--src/Canvas.js60
-rw-r--r--src/main.js6
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();
};