在动画HTML5画布中缩放和平移 [英] Zoom and pan in animated HTML5 canvas

查看:164
本文介绍了在动画HTML5画布中缩放和平移的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一张地图。我希望用户能够缩放和平移地图。想象一下谷歌地图,但不是无限地平移,地图是一个正方形(如果你超越它的边缘,它不会再次环绕)。

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 again if you go past the edge of it).

I使用 scale() translate()实现了缩放和平移。这些效果很好。

I have implemented zoom and pan using scale() and translate(). These work well.

我被困在最后一部分 - 当用户放大时,我想围绕该点居中放大。很难用语言来解释,所以想象一下当你在谷歌地图中鼠标滚动时会发生什么 - 这就是我想要的。

I am stuck on the final part - when a user zooms, I want to center the zoom around that point. It is hard to explain in words, so just imagine what happens when you mousewheel in Google Maps - that is what I want.

我已经查看了关于SO的每个答案标题中的任何这些术语。大多数是这一个的变体,其中基本上说这就是我需要做的事情:

I have looked at every answer on SO with any of these terms in the title. Most are variations on this one, which basically say this is what I need to do:

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 */);

然而,无论我做什么,我似乎无法让它发挥作用。我可以让它在缩放后缩放到特定点,但无论我做什么,我都无法使该点等于鼠标指针的位置。

However, no matter what I do, I cannot seem to get it to work. I can get it to zoom to a particular point after zooming, but whatever I do I cannot make that point equal to where the mouse pointer was.

查看这个小提琴。想象一下,这个正方形是一张地图而圆圈是国家或其他。

Check out this fiddle. Imagine the square is a map and the circles are countries or whatever.

我发现的最佳实现是此SO答案和链接的示例。但是,代码使用了SVG和 .createSVGMatrix()以及坦白说我无法理解的各种事情。如果可能的话,我更喜欢全画布解决方案。

The best implementation I have found is this SO answer and the linked example. However, the code makes use of SVG and .createSVGMatrix() and all sorts of things that, frankly, I can't understand. I would prefer a totally-canvas solution if possible.

显然我对使用库这样做感兴趣。我想知道为什么我正在做的事情不起作用。

Obviously I am not interested in doing this with a library. I want to understand why what I'm doing is not working.

推荐答案

这是一种缩放点的技巧:

绘制地图

通过不使用简化事情转换为绘制地图(无需翻译,缩放!)。

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

所需要的只是 context.drawImage 的缩放版本。

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

选择缩放点(焦点):

缩放焦点实际上是2个点!

The scaling focal point is actually 2 points!

第一个焦点是mouseX,mouseY,用户点击它来设置所需的缩放点。重要的是要记住鼠标坐标在缩放空间。用户正在看/点击的地图是缩放的,所以他们的mouseX,mouseY也被缩放。

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.

第二个焦点是c通过取消缩放鼠标坐标来计算。第二个点是原始未缩放地图上的等效鼠标位置。

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

缩放地图

当用户表示他们想要扩大或缩小地图时:

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


  • 计算新的缩放地图宽度和宽度高度

  • 计算从缩放点向上和向左拉动新缩放的地图所需的偏移量(缩放点先前由鼠标位置选择)。

代码:

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

以下是示例代码和演示:

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>

这篇关于在动画HTML5画布中缩放和平移的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆