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.