summaryrefslogtreecommitdiff
path: root/src/Canvas.js
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 /src/Canvas.js
parent62ccd5f48cd1f49effaa76ef01eb45072a0a42df (diff)
add zooming and panning
Diffstat (limited to 'src/Canvas.js')
-rw-r--r--src/Canvas.js95
1 files changed, 81 insertions, 14 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);
}
}