Zoom and pan in animated HTML5 canvas

前端 未结 1 1121
我寻月下人不归
我寻月下人不归 2020-11-30 12:54

I have a map. I want a user to be able to zoom and pan the map. Imagine Google Maps, but instead of being infinitely pannable, the map is a square (it doesn\'t wrap around a

相关标签:
1条回答
  • 2020-11-30 13:35

    Here's one technique for zooming at a point:

    Drawing the map

    Simplify things by not using transforms to draw the map (no need for translate,scale!).

    All that's needed is the scaling version of context.drawImage.

    What you do is scale the original map to the desired size and then pull it upward and leftward from the scaling point that the user has selected.

    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
    

    Selecting the scaling point (the focal point):

    The scaling focal point is actually 2 points!

    The first focal point is the mouseX,mouseY where the user clicked to set their desired scaling point. It's important to remember that the mouse coordinate is in scaled space. The map that the user is seeing/clicking is scaled so their mouseX,mouseY is scaled also.

    The second focal point is calculated by unscaling the mouse coordinate. This second point is the equivalent mouse position on the original unscaled map.

    The second unscaled focal point is used to calculate how much to pull the scaled map leftward and upward from the first focal point.

    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);
    }
    

    Scaling the map

    When the user indicates they want to scale the map larger or smaller:

    • calculate the new scaled map width & height
    • calculate how much offset is needed to pull the newly scaled map upward and leftward from the scaling point (the scaling point was previously selected by the mouse position).

    Code:

    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();
    }
    

    Here's example code and a Demo:

    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>

    0 讨论(0)
提交回复
热议问题