Proper map zoom

2014-09-01

Working on a game. In my engine you can zoom with the wheel, nothing special there. It's however very good for UI if the point of the mouse doesn't change. That is, if you are currently hovering over some coordinate, after zooming it would be best if the mouse is still hovering over the same coordinate.

This is doable, of course, and involves some annoying math. This is how I did it, quite verbose but it may help you with understanding the algo.

Code:
window.onmousewheel = function(e){
// zoom the map such that the point of the mouse remains stationary
// involves some pesky computations

// real tile size
var tw = tileSet.tileWidth;
var th = tileSet.tileHeight;
// to scaled
var stw = tw * this.scale;
var sth = th * this.scale;
// point of mouse in viewport (assuming your canvas originates at 0:0 of the browser)
var mx = e.clientX;
var my = e.clientY;
// normalized mouse coord to tile coord
var mtx = mx / stw;
var mty = my / sth;
// offset of viewport in tiles
var ox = this.viewport.x / stw;
var oy = this.viewport.y / sth;

// now update scale
if (e.wheelDeltaY > 0) {
this.scale += .25;
} else {
this.scale -= .25;
}

// new position of mouse, scaled, in tiles
var ntx = mx / (tw * this.scale);
var nty = my / (th * this.scale);

// difference must be subtracted from viewport offsets
var dtx = ntx - mtx;
var dty = nty - mty;

// move viewport such that the new extra width/height moves into the offset
// this causes the map point under the mouse not to move
this.viewport.x = (ox - dtx) * tw * this.scale;
this.viewport.y = (oy - dty) * th * this.scale;
}.bind(this);