First, I'd like to point out that from your code it looks like you're listening for 'mousewheel' events on the canvas. As noted here, the 'mousewheel' event is non-standard and is not on track to become a standard. As a result, you'll get results that are, at best, mixed when listening for it. The 'scroll' event is available on nearly every platform, and will likely be a better avenue for capturing user input.
As far as your question, you're on the right track for the behavior that you're looking for, but you're missing one step.
When you call scale
on a canvas context object, the behavior is very simple. Starting at the top left corner (0,0), the method scales the points of the canvas by the factors provided. Say you have a 10x10 canvas, and a black dot at 1,1. If the canvas is scaled by a factor of 2 on both axes, the 0,0 point will stay in the same place but point 1,1 will be where the point 2,2 was before scaling.
In order to achieve the 'zooming' behavior you're looking for, the context has to be translated after scaling so that the reference point occupies the same physical position it did before the scaling. In your case, the reference point is the point where the user's cursor sits when the zoom action is performed.
Luckily, the canvas context object provides a translate(x,y)
method that moves the origin of the context relative to the 0,0 point of the canvas. To translate it the correct ammount, you have to:
- Calculate the distance of the mouse cursor from the canvas origin before zooming
- Divide that distance by the scaling factor
- Translate the origin by that value
Since your code doesn't indicate the structure of your HTML, below I've marked it up with some comments and pseudocode to show how you might implement this algorithm:
//You'll need to get a reference to your canvas in order to calculate the relative position of
//the cursor to its top-left corner, and save it to a variable that is in scope inside of your
//event handler function
var canvas = document.getElementById('id_of_your_canvas');
//We're also going to set up a little helper function for returning an object indicating
//the position of the top left corner of your canvas element (or any other element)
function getElementOrigin(el){
var boundingBox = el.getBoundingClientRect();
return { x: boundingBox.left, y: boundingbox.top};
}
function OnMouseWheel (event) {
//you probably want to prevent scrolling from happening or from bubbling so:
event.preventDefault();
event.stopPropagation();
var delta = event.wheelDelta ? event.wheelDelta/40 : event.detail ? -event.detail : 0;
var canvasCorner = getElementOrigin(canvas)
//JavaScript doesn't offer a 'vector' or 'point' class natively but we don't need them
var mousePosition = {x: event.clientX, y: event.clientY};
var diff = {x: mousePostion.x - canvasCorner.x, y: mousePosition.y - canvasCorner.y};
var scaleFactor = 1.1;
if (delta) {
var factor = Math.pow(scaleFactor,delta);
var transX = (-1/factor) * diff.x;
var transY = (-1/factor) * diff.y;
context.scale(factor,factor);
context.translate(transX, transY);
}
}