summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2022-05-29 17:11:48 -0500
committersanine <sanine.not@pm.me>2022-05-29 17:11:48 -0500
commitbc6f5873f17ae10f42f87825ac64b136cfee3d72 (patch)
treef433186af60125c45ba63f545a556afc48c24e23
parent62ccd5f48cd1f49effaa76ef01eb45072a0a42df (diff)
add zooming and panning
-rw-r--r--src/Canvas.js95
-rw-r--r--src/main.js17
-rw-r--r--src/modules/Util.js6
3 files changed, 98 insertions, 20 deletions
diff --git a/src/Canvas.js b/src/Canvas.js
index 590034a..bd39f47 100644
--- a/src/Canvas.js
+++ b/src/Canvas.js
@@ -1,46 +1,113 @@
+import { clamp } from './modules/Util.js';
+
class Canvas {
constructor(rootId) {
const root = document.getElementById(rootId);
this.element = document.createElement('canvas');
this.context = this.element.getContext('2d');
- this.fillWindow();
- window.addEventListener('resize', () => this.fillWindow());
- root.appendChild(this.element);
+ /* state variables */
+ this.scale = 1;
+ this.zoom = 1;
+ const ZOOM_SPEED = 1.2;
+
+ this.mousePos = { x: 0, y: 0 };
+ this.pan = { x: 0, y: 0 };
+ this.panning = false;
- this.ondraw = null;
+ /* callbacks */
+ this.onDraw = null;
+ this.onMouseMove = null;
- /* automatically compute drawing-space mouse coordinates */
- this.mousePos = { x: 0, y: 0 };
- this.onmousemove = null;
+ /* register event listeners */
+
+ /* mouse movement */
this.element.addEventListener('mousemove', e => {
const matrix = this.context.getTransform();
matrix.invertSelf();
-
const screenX = e.offsetX; const screenY = e.offsetY;
- this.mousePos.x = matrix.a*screenX + matrix.b*screenY;
- this.mousePos.y = matrix.c*screenX + matrix.d*screenY;
- if (this.onmousemove) this.onmousemove(this.mousePos);
+ /* compute drawing-space position */
+ const x = matrix.a*screenX + matrix.b*screenY + matrix.e;
+ const y = matrix.c*screenX + matrix.d*screenY + matrix.f;
+
+ /* compute movement */
+ const dx = e.movementX / (this.zoom * this.scale);
+ const dy = e.movementY / (this.zoom * this.scale);
+
+ /* pan? */
+ if (this.panning) {
+ this.pan.x += dx;
+ this.pan.y += dy;
+ this.setTransform();
+ }
+
+ this.mousePos.x = x;
+ this.mousePos.y = y;
+
+ if (this.onMouseMove) this.onMouseMove(this.mousePos);
});
+ /* clicking */
+ this.element.addEventListener('mousedown', e => {
+ if (e.button === 1) this.panning = true;
+ });
+ this.element.addEventListener('mouseup', e => {
+ if (e.button === 1) this.panning = false;
+ });
+
+ /* mouse leave */
+ this.element.addEventListener('mouseleave', e => {
+ this.panning = false;
+ });
+
+ /* 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;
+ this.zoom *= delta;
+ this.zoom = clamp(this.zoom, 1, Infinity);
+ this.setTransform();
+ this.draw();
+ });
+
+ /* finalize setup */
+ this.fillWindow();
+ window.addEventListener('resize', () => this.fillWindow());
+ root.appendChild(this.element);
+ }
+
+ 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;
+ 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);
+ this.context.translate(this.pan.x, this.pan.y);
}
fillWindow() {
const width = window.innerWidth;
const height = window.innerHeight;
- const scale = Math.max(width, height);
+ this.scale = Math.max(width, height);
this.element.width = width; this.element.height = height;
- this.context.scale(scale, scale);
+ this.setTransform();
this.draw();
}
draw() {
this.context.clearRect(0, 0, 1, 1);
- if (this.ondraw) this.ondraw(this.context);
+ if (this.onDraw) this.onDraw(this.context);
}
}
diff --git a/src/main.js b/src/main.js
index 40c3f83..5c9b085 100644
--- a/src/main.js
+++ b/src/main.js
@@ -6,15 +6,22 @@ window.onload = () => {
const canvas = new Canvas('root');
const pos = canvas.mousePos;
- canvas.onmousemove = () => canvas.draw();
+ canvas.onMouseMove = () => canvas.draw();
- canvas.ondraw = ct => {
- ct.fillRect(0, 0, 0.5, 0.5);
+ canvas.onDraw = ct => {
+ ct.fillStyle = '#f00';
+ ct.fillRect(0, 0, 1/3, 1/3);
+ ct.fillStyle = '#0f0';
+ ct.fillRect(2/3, 0, 1/3, 1/3);
+ ct.fillStyle = '#ff0';
+ ct.fillRect(0, 2/3, 1/3, 1/3);
+ ct.fillStyle = '#00f';
+ ct.fillRect(2/3, 2/3, 1/3, 1/3);
ct.strokeStyle = '#fff';
- ct.lineWidth = 0.001;
+ ct.lineWidth = 1/(canvas.zoom * canvas.scale);
ct.beginPath();
- ct.arc(pos.x, pos.y, 0.1, 0, 2*Math.PI);
+ ct.arc(pos.x, pos.y, 30 /(canvas.zoom * canvas.scale), 0, 2*Math.PI);
ct.closePath();
ct.stroke();
};
diff --git a/src/modules/Util.js b/src/modules/Util.js
index cbe466d..2375f10 100644
--- a/src/modules/Util.js
+++ b/src/modules/Util.js
@@ -10,4 +10,8 @@ function useAverage() {
return [() => avg, append];
}
-export { useAverage };
+function clamp(value, min, max) {
+ return Math.min(Math.max(value, min), max);
+}
+
+export { useAverage, clamp };