Skip to content Skip to sidebar Skip to footer

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"