I'm trying to implement a zoom function on my canvas keeping the mouse position relative to where it was before.
This function accomplishes it almost. And here is a full example: https://codepen.io/PJEstrada006/pen/dyRxVXE?editors=0011
public zoom_wheel_canvas_translate(event): void {
// this is the illusionary point on UI that we wish to stay locked on
let point = this.mouse_position;
const wheel = event.deltaY < 0 ? 1 : -1;
// Compute zoom factor.
let zoomIntensity = 0.2;
let zoom = Math.exp(wheel * zoomIntensity);
this.scale = this.scale * zoom;
if (this.scale <= 1) {
this.canvas_ctx.setTransform(1, 0, 0, 1, 0, 0);
this.scale = 1;
return
}
if (this.scale >= 30) {
this.scale = 30
}
this.canvas_ctx.clearRect(
0,
0,
this.canvas_elm.width,
this.canvas_elm.height
);
let transform = this.canvas_ctx.getTransform();
this.canvas_ctx.resetTransform();
this.canvas_ctx.translate(point.x, point.y);
this.canvas_ctx.scale(zoom, zoom);
this.canvas_ctx.translate(-point.x, -point.y);
this.canvas_ctx.transform(transform.a, transform.b, transform.c, transform.d, transform.e, transform.f)
let transform_new = this.canvas_ctx.getTransform();
console.log('a', transform.a, 'd', transform.d)
console.log('e', transform.e, 'f', transform.f)
}
I have an image set as the background of the canvas that gets drawn like this:
ctx.drawImage(
this.image,
0,
0
)
The problem is that if I do something like the following:
The zoom out eventually ends up translating the canvas in the wrong way and my canvas image appears "sliced" or incomplete due to incorrect translations or scales. This happens just before the scale becomes 1, then it fixes itself because I have a resetTransform() call when scale = 1.
I'm expecting that no matter how I move the mouse I eventually zoom out and end up with the original transform matrix (identity matrix).
Can anyone help me spot what I'm doing wrong with my transforms. I don't quite get why it works perfect if I don't move the mouse, but stops working if I move the mouse in a zoomed in state and then zoom out.
You are looking to "understand why this mouse out algorithm is not working", I would not say it is not working as there are no errors, I tested the zoom on your codepen, and it feels fine to me...
Your zoom is relative to the position of the mouse, interacting with it feels intuitive and smooth.
I did some clean up to your code:
https://codepen.io/heldersepu/pen/LYjYEyL?editors=0010
The only issue I see is translating without considering that image could be out of the bounds,
see this code:
context.translate(x, y);
context.scale(zoom, zoom);
context.translate(-x, -y);
That code will be fine in a world map where the image is really large and wraps, I believe that google maps uses a similar logic, and it's a perfect use case there, but you are not considering the limitation of your image.
Zooming in your image we are not going to see the problem because the image is always padded, but going out you can be moving out of the bounds of the image if we are on the edge.
Here is an example:
the resulting canvas looks something like:
What to do on that case?
An option could be not to translate when we zoom out:
if (wheel > 0) context.translate(x, y);
context.scale(zoom, zoom);
if (wheel > 0) context.translate(-x, -y);
Or you could conditionally translate on one axis and not the other depending on the mouse position, but the correct solution is for you to decide, you have to test and see what you like.