Skip to content
Advertisement

Canvas zoom to cursor doesn’t work correct

I’m trying to scale canvas to/from mouse position, but it works not perfect (always goes few cells away from mouse pos). I’ve read all similar posts but i don’t know what’s wrong.

https://jsfiddle.net/shumikua/3afxtmqw/

All code is only to run snippet, it works good. Problem is in function scale.

function scale(svg, e){
        let x = e.originalEvent.offsetX - canvas[0].offsetLeft;
        let y = e.originalEvent.offsetY - canvas[0].offsetTop;
        let deltaY = e.originalEvent.deltaY;
        let scale_now = deltaY < 0 ? 1.5 : 1/1.5; 
        scaleFactor *= scale_now;
        panX = x - (x - panX)*scale_now;
        panY = y - (y - panY)*scale_now;
        
        draw();
    }

Advertisement

Answer

In function scale() keep the same values for x and y before and after scaling. You just need to update coordinates in screen panX and panY.

var canvas;
var ctx;
var ww;
var wh;
var scaleFactor;
var panX;
var panY;

function draw() {
  ctx.clearRect(0, 0, ww, wh);
  let size = Math.min((ww - 10) / 60, (wh - 10) / 60);
  let padding = {
    x: ww - size * 60,
    y: wh - size * 60,
  }
  ctx.save();
  ctx.translate(panX, panY);
  ctx.scale(scaleFactor, scaleFactor);
  for (let i = 0; i < 60; i++) {
    for (let j = 0; j < 60; j++) {
      ctx.fillStyle = '#f' + i + j + 'f';
      ctx.fillRect(padding.x / 2 + size * j, padding.y / 2 + size * i, size, size);
    }
  }
  ctx.restore();
}

function scale(svg, e) {
  let x = e.originalEvent.offsetX;
  let y = e.originalEvent.offsetY;
  let deltaY = e.originalEvent.deltaY;
  let scale_now = deltaY < 0 ? 1.5 : 1 / 1.5;
  scaleFactor *= scale_now;
  panX = x - (x - panX) * scale_now;
  panY = y - (y - panY) * scale_now;
  draw();
}

function move(svg, x, y) {
  panX += x;
  panY += y;
  draw();
}

function initialise() {
  ctx = canvas.get(0).getContext('2d');
  ww = canvas.outerWidth();
  wh = canvas.outerHeight();
  canvas.attr('width', ww);
  canvas.attr('height', wh);
  scaleFactor = 1.0;
  panX = 0;
  panY = 0;
  draw();
}
$(document).ready(function() {
  canvas = $('.canva');
  initialise();
  canvas.bind('mousewheel DOMMouseScroll', function(e) {
    e.preventDefault();
  });
  canvas.on('mousewheel DOMMouseScroll', function(e) {
    if (e.ctrlKey) {
      scale($(this), e);
    } else if (e.shiftKey) {
      move($(this), -e.originalEvent.deltaY / 5, 0);
    } else {
      move($(this), 0, -e.originalEvent.deltaY / 5);
    }
  });
  canvas.mousedown(function(e) {
    if (e.which !== 2) return;
    e.preventDefault();
    $(this).css('cursor', 'move');
    let old_x = e.offsetX;
    let old_y = e.offsetY;
    $(this).mousemove(function(emove) {
      let x = emove.offsetX;
      let y = emove.offsetY;
      move($(this), emove.offsetX - old_x, emove.offsetY - old_y);
      old_x = x;
      old_y = y;
    });
    $(this).mouseup(function() {
      $(this).off('mousemove');
      $(this).css('cursor', 'default');
    });
    $(this).mouseleave(function() {
      $(this).off('mousemove');
      $(this).css('cursor', 'default');
    });
  });
});
canvas {
  image-rendering: optimizeSpeed;
  /* Older versions of FF          */
  image-rendering: -moz-crisp-edges;
  /* FF 6.0+                       */
  image-rendering: -webkit-optimize-contrast;
  /* Safari                        */
  image-rendering: -o-crisp-edges;
  /* OS X & Windows Opera (12.02+) */
  image-rendering: pixelated;
  /* Awesome future-browsers       */
  -ms-interpolation-mode: nearest-neighbor;
  shape-rendering: crispEdges;
  border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="canvas" class="canva" width="200" height="200"></canvas>

Hold Ctrl and scroll wheel to scale.

User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement