问题描述
我有一张地图.我希望用户能够缩放和平移地图.想象一下 Google 地图,但地图不是无限可平移的,而是一个正方形(如果您越过它的边缘,它就不会再次环绕).
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).
我已经使用 scale()
和 translate()
实现了缩放和平移.这些效果很好.
I have implemented zoom and pan using scale()
and translate()
. These work well.
我被困在最后一部分 - 当用户缩放时,我想以该点为中心进行缩放.这很难用语言来解释,所以想象一下当您在 Google 地图中滚轮时会发生什么——这就是我想要的.
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.
我发现的最佳实现是 this SO answer 和链接的 示例.但是,代码使用了 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.
第二个焦点是通过缩放鼠标坐标来计算的.第二个点是原始未缩放地图上的等效鼠标位置.
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 画布中缩放和平移的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!