Using p5js, I'm working on a pan/zoom feature that would allow you to pan/zoom a image and then clamp the edges of the image. The main issue I'm having is with clamping the edges of the image so you can never drag the image past the edge of the container. For example:
Notice the black background in the unclamped image. When I'm dragging the image, it should never allow me to drag the image corners past the edges of the canvas.
Here's how I'm currently handling the various events and p5js setup:
/* eslint-disable no-undef, no-unused-vars */
var img;
var w, h, currentImageWidth, currentImageHeight;
var x, y, tox, toy;
var zoom = 0.01; //zoom step per mouse tick
function preload() {
img = loadImage("https://i.imgur.com/MBFJhju.jpeg", () => {});
}
function setup() {
createCanvas(600, 600);
w = currentImageWidth = 600;
h = currentImageHeight = 600;
x = tox = w / 2;
y = toy = h / 2;
}
function draw() {
background(0);
//tween/smooth motion
x = lerp(x, tox, 0.1);
y = lerp(y, toy, 0.1);
w = lerp(w, currentImageWidth, 0.1);
h = lerp(h, currentImageHeight, 0.1);
image(img, x - w / 2, y - h / 2, w, h);
}
function mouseDragged() {
let maxX = currentImageWidth;
let minX = -1 * maxX;
let maxY = currentImageHeight;
let minY = -1 * maxY;
let nextXposition = tox + mouseX - pmouseX;
let nextYposition = toy + mouseY - pmouseY;
if (
nextXposition - currentImageWidth * 0.5 < maxX &&
nextXposition - currentImageWidth * 0.5 > minX
) {
tox += mouseX - pmouseX;
}
if (
nextYposition - currentImageHeight * 0.5 < maxY &&
nextYposition - currentImageHeight * 0.5 > minY
) {
toy += mouseY - pmouseY;
}
}
function mouseWheel(event) {
var e = event.wheelDeltaY;
console.log(event);
if (e > 0) {
//zoom in
for (let i = 0; i < e; i++) {
if (currentImageWidth > 3 * width) return; //max zoom
tox -= zoom * (mouseX - tox);
toy -= zoom * (mouseY - toy);
currentImageWidth *= zoom + 1;
currentImageHeight *= zoom + 1;
}
}
if (e < 0) {
//zoom out
for (let i = 0; i < -e; i++) {
if (currentImageWidth < width) return; //min zoom
tox += (zoom / (zoom + 1)) * (mouseX - tox);
toy += (zoom / (zoom + 1)) * (mouseY - toy);
currentImageHeight /= zoom + 1;
currentImageWidth /= zoom + 1;
}
}
//adjust x and y if out of bounds
let maxX = width * 0.5;
let minX = -1 * maxX;
let maxY = height * 0.5;
let minY = -1 * maxY;
let diff = 0;
if (currentImageWidth + currentImageWidth * 0.5 <= maxX) {
diff = maxX - (tox + currentImageWidth * 0.5);
tox += diff;
}
if (currentImageWidth - currentImageWidth * 0.5 >= minX) {
diff = currentImageWidth - currentImageWidth * 0.5 - minX;
tox -= diff;
}
if (toy + currentImageHeight * 0.5 <= maxY) {
diff = maxY - (toy + currentImageHeight * 0.5);
toy += diff;
}
if (toy - currentImageHeight * 0.5 >= minY) {
diff = toy - currentImageHeight * 0.5 - minY;
toy -= diff;
}
return false;
}
Currently, the clamp doesn't actually work. But you can drag the image and zoom. My intentions are for this to work like pan/zoom on a map in a game.
I believe the main pain point would be the mouseDragged event handler, and I think the way I'll handle clamping with the mouseWheel event handler will work for zoom events.
Please let me know if you need any additional info!
Full demo:
https://codesandbox.io/embed/great-dirac-yvd0s?fontsize=14&hidenavigation=1&theme=dark

