I'm trying to implement zoom feature using p5.js. Current zoom level and x and y positions are stored in controls.view
object. Default or (0, 0) position is at top left corner. Problem is to adjust x and y position values on zoom in/out so that no matter what the current position of the view is, it will stay at point of zoom or mouse cursor.
Scaling and translating is executed in draw
function by x, y and zoom values so those values must be modified directly (can't run translate on canvas directly). Here is Codepen.
let canvas, circles;
const controls = {
view: {
x: 0,
y: 0,
zoom: 1
},
viewPos: {
prevX: null,
prevY: null,
isDragging: false
},
}
function setup() {
canvas = createCanvas(window.innerWidth, window.innerHeight);
canvas.mouseWheel(e => Controls.zoom(controls).worldZoom(e))
circles = Circle.create(100)
}
function draw() {
background(100)
translate(controls.view.x, controls.view.y);
scale(controls.view.zoom)
circles.forEach(circle => circle.show());
}
window.mousePressed = e => Controls.move(controls).mousePressed(e)
window.mouseDragged = e => Controls.move(controls).mouseDragged(e);
window.mouseReleased = e => Controls.move(controls).mouseReleased(e)
class Controls {
static move(controls) {
function mousePressed(e) {
controls.viewPos.isDragging = true;
controls.viewPos.prevX = e.clientX;
controls.viewPos.prevY = e.clientY;
}
function mouseDragged(e) {
const {
prevX,
prevY,
isDragging
} = controls.viewPos;
if (!isDragging) return;
const pos = {
x: e.clientX,
y: e.clientY
};
const dx = pos.x - prevX;
const dy = pos.y - prevY;
if (prevX || prevY) {
controls.view.x += dx;
controls.view.y += dy;
controls.viewPos.prevX = pos.x, controls.viewPos.prevY = pos.y
}
}
function mouseReleased(e) {
controls.viewPos.isDragging = false;
controls.viewPos.prevX = null;
controls.viewPos.prevY = null;
}
return {
mousePressed,
mouseDragged,
mouseReleased
}
}
static zoom(controls) {
// function calcPos(x, y, zoom) {
// const newX = width - (width * zoom - x);
// const newY = height - (height * zoom - y);
// return {x: newX, y: newY}
// }
function worldZoom(e) {
const {
x,
y,
deltaY
} = e;
const direction = deltaY > 0 ? -1 : 1;
const factor = 0.1;
const zoom = 1 * direction * factor;
controls.view.zoom += zoom;
controls.view.x += -(x * direction * factor);
controls.view.y += -(y * direction * factor);
}
return {
worldZoom
}
}
}
class Circle {
constructor(x, y) {
this.x = x;
this.y = y;
}
show() {
fill(255);
noStroke();
ellipse(this.x, this.y, 15, 15);
}
static create(count) {
return Array.from(Array(count), () => {
const x = random(-500, width + 500);
const y = random(-500, height + 500);
return new this(x, y);
})
}
}
body {margin: 0; padding: 0;}
canvas {vertical-align: top;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.0/p5.min.js"></script>
function worldZoom(e) {
const {x, y, deltaY} = e;
const direction = deltaY > 0 ? -1 : 1;
const factor = 0.1;
const zoom = 1 * direction * factor;
controls.view.zoom += zoom;
controls.view.x += -(x * direction * factor);
controls.view.y += -(y * direction * factor);
}
This are some of the similar questions that I tried to implement: Zoom Canvas to Mouse Cursor, Zoom in on a point (using scale and translate)