Zoom de canvas para o cursor do mouse

Estou programando um projeto HTML5 que envolve o zoom dentro e fora das imagens usando a roda de rolagem. Eu quero aumentar o zoom para o cursor como o google maps, mas estou completamente perdido em como calcular os movimentos.

O que eu tenho: image x e y (canto superior esquerdo); largura e altura da imagem; cursor xey em relação ao centro da canvas.

    Em suma, você quer translate() o contexto da canvas pelo seu deslocamento, scale() lo scale() para ampliar ou reduzir e, em seguida, translate() volta pelo oposto do deslocamento do mouse. Observe que você precisa transformar a posição do cursor do espaço da canvas no contexto da canvas transformada.

     ctx.translate(pt.x,pt.y); ctx.scale(factor,factor); ctx.translate(-pt.x,-pt.y); 

    Demonstração: http://phrogz.net/tmp/canvas_zoom_to_cursor.html

    Eu coloquei um exemplo completo de trabalho no meu site para você examinar, suportando o arrastamento, clique para aumentar o zoom, clique com a tecla Shift para fora ou use a roda de rolagem para cima / baixo.

    O único problema (atual) é que o Safari aumenta muito o zoom em comparação com o Chrome ou o Firefox.

    Espero que essas bibliotecas JS ajudem você: (HTML5, JS)

    1. Lupa

    http://www.netzgesta.de/loupe/

    1. CanvasZoom

    https://github.com/akademy/CanvasZoom

    1. Rolador

    https://github.com/zynga/scroller

    Quanto a mim, estou usando a lupa. É incrivel! Para você, o melhor caso – scroller.

    Eu peguei a resposta de @ Phrogz como base e fiz uma pequena biblioteca que permite a canvas arrastar, aplicar zoom e girar. Aqui está o exemplo.

     var canvas = document.getElementById('canvas') //assuming that @param draw is a function where you do your main drawing. var control = new CanvasManipulation(canvas, draw) control.init() control.layout() //now you can drag, zoom and rotate in canvas 

    Você pode encontrar exemplos e documentação mais detalhados na página do projeto

    Recentemente, precisei arquivar os mesmos resultados que Phrogz já havia feito, mas, em vez de usar context.scale() , calculei o tamanho de cada object com base na proporção.

    Isso é o que eu criei. A lógica por trás disso é muito simples. Antes de dimensionar, eu calculo a distância do ponto da borda em porcentagens e depois ajustei a porta de visualização para o local correto.

    Demorei um bom tempo para pensar nisso, espero que poupe tempo para os outros.

     $(function () { var canvas = $('canvas.main').get(0) var canvasContext = canvas.getContext('2d') var ratio = 1 var vpx = 0 var vpy = 0 var vpw = window.innerWidth var vph = window.innerHeight var orig_width = 4000 var orig_height = 4000 var width = 4000 var height = 4000 $(window).on('resize', function () { $(canvas).prop({ width: window.innerWidth, height: window.innerHeight, }) }).trigger('resize') $(canvas).on('wheel', function (ev) { ev.preventDefault() // for stackoverflow var step if (ev.originalEvent.wheelDelta) { step = (ev.originalEvent.wheelDelta > 0) ? 0.05 : -0.05 } if (ev.originalEvent.deltaY) { step = (ev.originalEvent.deltaY > 0) ? 0.05 : -0.05 } if (!step) return false // yea.. var new_ratio = ratio + step var min_ratio = Math.max(vpw / orig_width, vph / orig_height) var max_ratio = 3.0 if (new_ratio < min_ratio) { new_ratio = min_ratio } if (new_ratio > max_ratio) { new_ratio = max_ratio } // zoom center point var targetX = ev.originalEvent.clientX || (vpw / 2) var targetY = ev.originalEvent.clientY || (vph / 2) // percentages from side var pX = ((vpx * -1) + targetX) * 100 / width var pY = ((vpy * -1) + targetY) * 100 / height // update ratio and dimentsions ratio = new_ratio width = orig_width * new_ratio height = orig_height * new_ratio // translate view back to center point var x = ((width * pX / 100) - targetX) var y = ((height * pY / 100) - targetY) // don't let viewport go over edges if (x < 0) { x = 0 } if (x + vpw > width) { x = width - vpw } if (y < 0) { y = 0 } if (y + vph > height) { y = height - vph } vpx = x * -1 vpy = y * -1 }) var is_down, is_drag, last_drag $(canvas).on('mousedown', function (ev) { is_down = true is_drag = false last_drag = { x: ev.clientX, y: ev.clientY } }) $(canvas).on('mousemove', function (ev) { is_drag = true if (is_down) { var x = vpx - (last_drag.x - ev.clientX) var y = vpy - (last_drag.y - ev.clientY) if (x < = 0 && vpw < x + width) { vpx = x } if (y <= 0 && vph < y + height) { vpy = y } last_drag = { x: ev.clientX, y: ev.clientY } } }) $(canvas).on('mouseup', function (ev) { is_down = false last_drag = null var was_click = !is_drag is_drag = false if (was_click) { } }) $(canvas).css({ position: 'absolute', top: 0, left: 0 }).appendTo(document.body) function animate () { window.requestAnimationFrame(animate) canvasContext.clearRect(0, 0, canvas.width, canvas.height) canvasContext.lineWidth = 1 canvasContext.strokeStyle = '#ccc' var step = 100 * ratio for (var x = vpx; x < width + vpx; x += step) { canvasContext.beginPath() canvasContext.moveTo(x, vpy) canvasContext.lineTo(x, vpy + height) canvasContext.stroke() } for (var y = vpy; y < height + vpy; y += step) { canvasContext.beginPath() canvasContext.moveTo(vpx, y) canvasContext.lineTo(vpx + width, y) canvasContext.stroke() } canvasContext.strokeRect(vpx, vpy, width, height) canvasContext.beginPath() canvasContext.moveTo(vpx, vpy) canvasContext.lineTo(vpx + width, vpy + height) canvasContext.stroke() canvasContext.beginPath() canvasContext.moveTo(vpx + width, vpy) canvasContext.lineTo(vpx, vpy + height) canvasContext.stroke() canvasContext.restore() } animate() }) 
     < !DOCTYPE html>