html5-canvas zoom panning

html5 canvas - Zoom y panorámica en lienzo animado HTML5



html5-canvas panning (1)

Aquí hay una técnica para hacer zoom en un punto:

Dibujando el mapa

Simplifique las cosas al no usar transformaciones para dibujar el mapa (¡no es necesario traducir, escalar!).

Todo lo que se necesita es la versión de escala de context.drawImage .

Lo que debe hacer es escalar el mapa original al tamaño deseado y luego tirarlo hacia arriba y hacia la izquierda desde el punto de escala que el usuario ha seleccionado.

context.drawImage( map, 0,0,map.width,map.height, // start with the map at original (unscaled) size offsetX,offsetY, // pull the map leftward & upward from the scaling point scaledWidth,scaledHeight // resize the map to the currently scaled size

Selección del punto de escala (el punto focal):

¡El punto focal de escala es en realidad 2 puntos!

El primer punto focal es el mouseX, mouseY, donde el usuario hizo clic para establecer el punto de escala deseado. Es importante recordar que la coordenada del mouse está en un espacio escalado . El mapa que el usuario está viendo / haciendo clic se escala para que su mouseX, mouseY también se escale.

El segundo punto focal se calcula descalcificando la coordenada del mouse. Este segundo punto es la posición equivalente del mouse en el mapa original sin escala.

El segundo punto focal sin escala se usa para calcular cuánto tirar del mapa escalado hacia la izquierda y hacia arriba desde el primer punto focal.

function setFocus(mx,my){ // mouseX,mouseY is the scaling point in scaled coordinates focusX=mx; focusY=my; // convert the scaled focal point // to an unscaled focal point focusX1=parseInt((mx-mapLeft)/scale); focusY1=parseInt((my-mapTop)/scale); }

Escalando el mapa

Cuando el usuario indica que quiere escalar el mapa más grande o más pequeño:

  • calcular el nuevo ancho y alto del mapa escalado
  • calcule cuánto desplazamiento se necesita para tirar del mapa recién escalado hacia arriba y hacia la izquierda desde el punto de escala (el punto de escala fue seleccionado previamente por la posición del mouse).

Código:

function setScale(newScale){ scale=newScale; // calc the width & height of the newly scaled map mapWidth=parseInt(iw*scale); mapHeight=parseInt(ih*scale); // calc how much to offset the map on the canvas mapLeft=parseInt(focusX-focusX1*scale); mapTop =parseInt(focusY-focusY1*scale); // draw the map drawMap(); }

Aquí hay un código de ejemplo y una demostración:

var canvas=document.getElementById("canvas"); var ctx=canvas.getContext("2d"); var $canvas=$("#canvas"); var canvasOffset=$canvas.offset(); var offsetX=canvasOffset.left; var offsetY=canvasOffset.top; // var counter=1; var PI2=Math.PI*2; var iw,ih; var mapLeft,mapTop,mapWidth,mapHeight; var focusX,focusY,focusX1,focusY1; var scale; var map=new Image(); map.onload=start; map.src="https://dl.dropboxusercontent.com/u/139992952/multple/mapSmall.png"; function start(){ iw=map.width; ih=map.height; // initial mapLeft=0; mapTop=0; scale=1.00; setFocus(iw/2*scale,ih/2*scale); setScale(scale); // also sets mapWidth,mapHeight drawMap(); // $("#canvas").mousedown(function(e){handleMouseDown(e);}); // canvas.addEventListener(''DOMMouseScroll'',handleScroll,false); canvas.addEventListener(''mousewheel'',handleScroll,false); } // function setScale(newScale){ scale=newScale; mapWidth=parseInt(iw*scale); mapHeight=parseInt(ih*scale); mapLeft=parseInt(focusX-focusX1*scale); mapTop =parseInt(focusY-focusY1*scale); drawMap(); } // function setFocus(mx,my){ // mouseX,mouseY is the scaling point in scaled coordinates focusX=mx; focusY=my; // convert the scaled focal point // to an unscaled focal point focusX1=parseInt((mx-mapLeft)/scale); focusY1=parseInt((my-mapTop)/scale); // drawMap(); } // function drawMap(){ ctx.clearRect(0,0,canvas.width,canvas.height); ctx.save(); ctx.drawImage(map,0,0,iw,ih,mapLeft,mapTop,mapWidth,mapHeight); dot(ctx,focusX,focusY,"red"); ctx.restore(); } function dot(ctx,x,y,fill){ ctx.beginPath(); ctx.arc(x,y,4,0,PI2); ctx.closePath(); ctx.fillStyle=fill; ctx.fill(); ctx.lineWidth=2; ctx.stroke(); } // function handleScroll(e){ e.preventDefault(); e.stopPropagation(); var delta=e.wheelDelta?e.wheelDelta/30:e.detail?-e.detail:0; if (delta){ counter+=delta; setScale(1+counter/100); } }; // function handleMouseDown(e){ e.preventDefault(); e.stopPropagation(); mouseX=parseInt(e.clientX-offsetX); mouseY=parseInt(e.clientY-offsetY); setFocus(mouseX,mouseY); drawMap(); }

body{ background-color: ivory; } canvas{border:1px solid red;}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <h4>Click to set zoom point<br>Use mousewheel to zoom</h4> <canvas id="canvas" width=600 height=400></canvas><br>

Tengo un mapa Quiero que un usuario pueda hacer zoom y desplazar el mapa. Imagina Google Maps, pero en lugar de ser infinitamente panorámico, el mapa es un cuadrado (no se vuelve a envolver si pasas el borde).

He implementado el zoom y la panorámica usando scale() y translate() . Estos funcionan bien.

Estoy atascado en la parte final: cuando un usuario hace zoom, quiero centrar el zoom alrededor de ese punto. Es difícil de explicar con palabras, así que imagínense lo que sucede cuando mueven el mouse en Google Maps: eso es lo que quiero.

He visto todas las respuestas en SO con cualquiera de estos términos en el título. La mayoría son variaciones de este , que básicamente dicen que esto es lo que necesito hacer:

ctx.translate(/* to the point where the mouse is */); ctx.scale(/* to zoom level I want */) ctx.translate(/* back to the point where the mouse was, taking zoom into account */);

Sin embargo, no importa lo que haga, parece que no puedo hacer que funcione. Puedo hacer que se acerque a un punto en particular después del zoom, pero haga lo que haga, no puedo hacer que ese punto sea igual al del puntero del mouse.

Mira este violín . Imagina que el cuadrado es un mapa y los círculos son países o lo que sea.

La mejor implementación que he encontrado es esta respuesta SO y el example vinculado. Sin embargo, el código hace uso de SVG y .createSVGMatrix() y todo tipo de cosas que, francamente, no puedo entender. Preferiría una solución totalmente lienzo si es posible.

Obviamente no estoy interesado en hacer esto con una biblioteca. Quiero entender por qué lo que estoy haciendo no funciona.