Javascript/canvas, Map Style Point Zooming
I have a canvas with a bunch of objects. I have a zoom function that bumps up a zoom variable by which every coordinate is multiplied. I want to be able to point at a coordinate an
Solution 1:
Scale at coordinate
If given a screen coordinate that has scaled content you need to move the origin towards or away from that point at an amount that matches the zoom.
If you zoom in the origin moves towards the position of the mouse. and zoom out away.
So with the mouse get the x,y position and depending on the wheel direction the amount to zoom in or out
// e is the mouse wheel event
const x = e.offsetX;
const y = e.offsetY;
const amount = e.wheelDelta > 0 ? 1.1 : 1 / 1.1;
Then you apply that to the current scale and move the origin to match
scale *= amount; // the new scale
// move the origin
origin.x = x - (x - origin.x) * amount;
origin.y = y - (y - origin.y) * amount;
You can then set the 2D position and scale with
ctx.setTransform(scale, 0, 0, scale, origin.x, origin.y);
Example
Below is a simple view example using bits of the code from your fiddle.
The object view
keeps track of the current view and maintains a matrix that can be applied to the canvas.
const ctx = canvas.getContext("2d");
canvas.width = 500;
canvas.height = 500;
const randI = (min, max = min + (min = 0)) => (Math.random() * (max - min) + min) | 0;
const rand = (min, max = min + (min = 0)) => Math.random() * (max - min) + min;
const objects = [];
for (let i = 0; i < 100; i++) {
objects.push({
x: rand(canvas.width),
y: rand(canvas.height),
w: rand(40),
h: rand(40),
col: `rgb(${randI(255)},${randI(255)},${randI(255)})`,
});
}
canvas.addEventListener("mousewheel", onmousewheel, false);
canvas.addEventListener("DOMMouseScroll", onmousewheel, false);
requestAnimationFrame(drawCanvas); // this will call drawcanvas after all other code has run
const view = (() => {
const matrix = [1, 0, 0, 1, 0, 0]; // current view transform
var m = matrix; // alias for clear code
var scale = 1; // current scale
var ctx; // reference to the 2D context
const pos = { x: 0, y: 0 }; // current position of origin
var dirty = true;
const API = {
setContext(_ctx) { ctx = _ctx; dirty = true },
apply() {
if (dirty) { this.update() }
ctx.setTransform(m[0], m[1], m[2], m[3], m[4], m[5])
},
getScale() { return scale },
getPosition() { return pos },
isDirty() { return dirty },
update() {
dirty = false;
m[3] = m[0] = scale;
m[2] = m[1] = 0;
m[4] = pos.x;
m[5] = pos.y;
},
scaleAt(at, amount) { // at in screen coords
if (dirty) { this.update() }
scale *= amount;
pos.x = at.x - (at.x - pos.x) * amount;
pos.y = at.y - (at.y - pos.y) * amount;
dirty = true;
},
};
return API;
})();
view.setContext(ctx);
function drawCanvas() {
if (view.isDirty()) { // has the view changed, then draw all
ctx.setTransform(1, 0, 0, 1, 0, 0); // default transform for clear
ctx.clearRect(0, 0, canvas.width, canvas.height);
view.apply(); // set the 2D context transform to the view
for (i = 0; i < objects.length; i++) {
var obj = objects[i];
ctx.fillStyle = obj.col;
ctx.fillRect(obj.x, obj.y, obj.h, obj.h);
}
}
requestAnimationFrame(drawCanvas);
}
function onmousewheel(event) {
var e = window.event || event;
var x = e.offsetX;
var y = e.offsetY;
const delta = e.type === "mousewheel" ? e.wheelDelta : -e.detail;
if (delta > 0) { view.scaleAt({x, y}, 1.1) }
else { view.scaleAt({x, y}, 1 / 1.1) }
e.preventDefault();
}
#canvas {
border: 2px solid;
}
<canvas id="canvas"></canvas>
Post a Comment for "Javascript/canvas, Map Style Point Zooming"