Javascript Google Maps v3 - 我能确保每次都能顺利平移吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3817812/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Google Maps v3 - can I ensure smooth panning every time?
提问by Kai Qing
My map has several hundred markers within a city. Usually no more than a 20 mile radius. I've read through the documentation and haven't found a way to set the init to automatically pan between every marker, no matter the distance. The default behavior is to pan if close, jump if far. I understand why they would do this since the map doesn't load the whole world at the selected zoom level and it could screw up if the distance was too great. However, I think it could handle 20 mile radius with minimal complaints.
我的地图在一个城市中有数百个标记。通常不超过 20 英里半径。我已经通读了文档,但还没有找到一种方法来将 init 设置为在每个标记之间自动平移,无论距离多远。默认行为是如果靠近则平移,如果远则跳转。我理解他们为什么会这样做,因为地图不会以选定的缩放级别加载整个世界,如果距离太大,它可能会搞砸。但是,我认为它可以以最少的投诉处理 20 英里的半径。
If anyone has any ideas, I'd love to hear them. Thanks
如果有人有任何想法,我很乐意听取他们的意见。谢谢
回答by Daniel Vassallo
The threshold of the smooth panning does not depend on the distance between the current center and the new target. It depends on whether the change will require a full page scroll (horizontally and vertically) or not:
平滑平移的阈值不依赖于当前中心与新目标之间的距离。这取决于更改是否需要整页滚动(水平和垂直):
Quoting from the API Reference:
引用API 参考:
panTo(latLng:LatLng)
Changes the center of the map to the given LatLng. If the change is less than both the width and height of the map, the transition will be smoothly animated.
panTo(latLng:LatLng)
将地图中心更改为给定的 LatLng。如果变化小于地图的宽度和高度,过渡将平滑动画。
Therefore, as long as you are zoomed out such that your viewport is 20 miles in height and width, you should be guaranteed smooth panning for distances under 20 miles.
因此,只要您缩小到视口的高度和宽度为 20 英里,就应该保证在 20 英里以下的距离内平滑平移。
回答by Darren Hicks
Here's a solution that pans smoothly and also allows for other click requests to be queue'd up while a previous pan is already in progress:
这是一个平滑平移的解决方案,还允许在前一个平移正在进行时将其他点击请求排队:
var panPath = []; // An array of points the current panning action will use
var panQueue = []; // An array of subsequent panTo actions to take
var STEPS = 50; // The number of steps that each panTo action will undergo
function panTo(newLat, newLng) {
if (panPath.length > 0) {
// We are already panning...queue this up for next move
panQueue.push([newLat, newLng]);
} else {
// Lets compute the points we'll use
panPath.push("LAZY SYNCRONIZED LOCK"); // make length non-zero - 'release' this before calling setTimeout
var curLat = map.getCenter().lat();
var curLng = map.getCenter().lng();
var dLat = (newLat - curLat)/STEPS;
var dLng = (newLng - curLng)/STEPS;
for (var i=0; i < STEPS; i++) {
panPath.push([curLat + dLat * i, curLng + dLng * i]);
}
panPath.push([newLat, newLng]);
panPath.shift(); // LAZY SYNCRONIZED LOCK
setTimeout(doPan, 20);
}
}
function doPan() {
var next = panPath.shift();
if (next != null) {
// Continue our current pan action
map.panTo( new google.maps.LatLng(next[0], next[1]));
setTimeout(doPan, 20 );
} else {
// We are finished with this pan - check if there are any queue'd up locations to pan to
var queued = panQueue.shift();
if (queued != null) {
panTo(queued[0], queued[1]);
}
}
}
回答by tato.rodrigo
We developed a workaround to smoothly animate the panTo
in all cases.
我们开发了一种解决方法,可以panTo
在所有情况下平滑地设置动画。
Basically in cases that the native panTo
will not animate smoothly, we zoom out
, panTo
and zoom in
to the destination location.
基本上,在原生panTo
动画不能流畅的情况下,我们zoom out
,panTo
并zoom in
到达目标位置。
To use the code below, call smoothlyAnimatePanTo
passing the map
instance as first parameter and the destination latLng
as second parameter.
要使用下面的代码,调用smoothlyAnimatePanTo
将map
实例作为第一个参数传递,将目标latLng
作为第二个参数传递。
There is a jsfiddle to demonstrate this solution in action here. Just edit the script
tag to put your own google maps javascript api key.
有一个的jsfiddle证明在行动该解决方案在这里。只需编辑script
标签以放置您自己的谷歌地图 javascript api 密钥。
Any comments and contributions will be welcome.
欢迎任何评论和贡献。
/**
* Handy functions to project lat/lng to pixel
* Extracted from: https://developers.google.com/maps/documentation/javascript/examples/map-coordinates
**/
function project(latLng) {
var TILE_SIZE = 256
var siny = Math.sin(latLng.lat() * Math.PI / 180)
// Truncating to 0.9999 effectively limits latitude to 89.189. This is
// about a third of a tile past the edge of the world tile.
siny = Math.min(Math.max(siny, -0.9999), 0.9999)
return new google.maps.Point(
TILE_SIZE * (0.5 + latLng.lng() / 360),
TILE_SIZE * (0.5 - Math.log((1 + siny) / (1 - siny)) / (4 * Math.PI)))
}
/**
* Handy functions to project lat/lng to pixel
* Extracted from: https://developers.google.com/maps/documentation/javascript/examples/map-coordinates
**/
function getPixel(latLng, zoom) {
var scale = 1 << zoom
var worldCoordinate = project(latLng)
return new google.maps.Point(
Math.floor(worldCoordinate.x * scale),
Math.floor(worldCoordinate.y * scale))
}
/**
* Given a map, return the map dimension (width and height)
* in pixels.
**/
function getMapDimenInPixels(map) {
var zoom = map.getZoom()
var bounds = map.getBounds()
var southWestPixel = getPixel(bounds.getSouthWest(), zoom)
var northEastPixel = getPixel(bounds.getNorthEast(), zoom)
return {
width: Math.abs(southWestPixel.x - northEastPixel.x),
height: Math.abs(southWestPixel.y - northEastPixel.y)
}
}
/**
* Given a map and a destLatLng returns true if calling
* map.panTo(destLatLng) will be smoothly animated or false
* otherwise.
*
* optionalZoomLevel can be optionally be provided and if so
* returns true if map.panTo(destLatLng) would be smoothly animated
* at optionalZoomLevel.
**/
function willAnimatePanTo(map, destLatLng, optionalZoomLevel) {
var dimen = getMapDimenInPixels(map)
var mapCenter = map.getCenter()
optionalZoomLevel = !!optionalZoomLevel ? optionalZoomLevel : map.getZoom()
var destPixel = getPixel(destLatLng, optionalZoomLevel)
var mapPixel = getPixel(mapCenter, optionalZoomLevel)
var diffX = Math.abs(destPixel.x - mapPixel.x)
var diffY = Math.abs(destPixel.y - mapPixel.y)
return diffX < dimen.width && diffY < dimen.height
}
/**
* Returns the optimal zoom value when animating
* the zoom out.
*
* The maximum change will be currentZoom - 3.
* Changing the zoom with a difference greater than
* 3 levels will cause the map to "jump" and not
* smoothly animate.
*
* Unfortunately the magical number "3" was empirically
* determined as we could not find any official docs
* about it.
**/
function getOptimalZoomOut(map, latLng, currentZoom) {
if(willAnimatePanTo(map, latLng, currentZoom - 1)) {
return currentZoom - 1
} else if(willAnimatePanTo(map, latLng, currentZoom - 2)) {
return currentZoom - 2
} else {
return currentZoom - 3
}
}
/**
* Given a map and a destLatLng, smoothly animates the map center to
* destLatLng by zooming out until distance (in pixels) between map center
* and destLatLng are less than map width and height, then panTo to destLatLng
* and finally animate to restore the initial zoom.
*
* optionalAnimationEndCallback can be optionally be provided and if so
* it will be called when the animation ends
**/
function smoothlyAnimatePanToWorkarround(map, destLatLng, optionalAnimationEndCallback) {
var initialZoom = map.getZoom(), listener
function zoomIn() {
if(map.getZoom() < initialZoom) {
map.setZoom(Math.min(map.getZoom() + 3, initialZoom))
} else {
google.maps.event.removeListener(listener)
//here you should (re?)enable only the ui controls that make sense to your app
map.setOptions({draggable: true, zoomControl: true, scrollwheel: true, disableDoubleClickZoom: false})
if(!!optionalAnimationEndCallback) {
optionalAnimationEndCallback()
}
}
}
function zoomOut() {
if(willAnimatePanTo(map, destLatLng)) {
google.maps.event.removeListener(listener)
listener = google.maps.event.addListener(map, 'idle', zoomIn)
map.panTo(destLatLng)
} else {
map.setZoom(getOptimalZoomOut(map, destLatLng, map.getZoom()))
}
}
//here you should disable all the ui controls that your app uses
map.setOptions({draggable: false, zoomControl: false, scrollwheel: false, disableDoubleClickZoom: true})
map.setZoom(getOptimalZoomOut(map, destLatLng, initialZoom))
listener = google.maps.event.addListener(map, 'idle', zoomOut)
}
function smoothlyAnimatePanTo(map, destLatLng) {
if(willAnimatePanTo(map, destLatLng)) {
map.panTo(destLatLng)
} else {
smoothlyAnimatePanToWorkarround(map, destLatLng)
}
}
回答by Brad P.
See this other SO answer about using javascript's setInterval
function to create a periodic function that calls panBy
on your map: Can Google Maps be set to a slow constant pan? Like a globe revolution?
请参阅有关使用 javascript 的setInterval
函数创建panBy
在地图上调用的周期性函数的其他 SO 答案:Can Google Maps be set to a slow constant pan? 就像一场全球革命?
This can be used to pan the map by x pixels on each call to panBy, allowing you to slow down the panBy rate (since you are only telling gmaps to panTo a short distance).
这可用于在每次调用 panBy 时将地图平移 x 像素,允许您减慢 panBy 速率(因为您只是告诉 gmaps panTo 短距离)。
回答by levik
As Daniel has mentioned, the built-in panTo() function will not work for you if the two points are too far apart. You can manually animate it yourself if that's the case though: for each zoom level, figure out the distance covered by say 100 pixels. Now, when you have to pan to a point, you can use this information to figure out if the panTo() funciton will animate or jump. If the distance moved is so big that it will not animate, you should do the animation manually - compute some intermediate waypoints between your current map center and your destination, and pan to them in sequence.
正如 Daniel 所提到的,如果两点相距太远,内置的 panTo() 函数将不起作用。如果是这种情况,您可以自己手动设置动画:对于每个缩放级别,计算出 100 像素覆盖的距离。现在,当您必须平移到某个点时,您可以使用此信息来确定 panTo() 函数是否会产生动画或跳跃。如果移动的距离太大以至于不会动画,您应该手动制作动画 - 计算当前地图中心和目的地之间的一些中间航点,并按顺序平移到它们。
回答by JohnDoeTheFifth
@tato.rodrigo
@tato.rodrigo
I don't have enough reputation to post as an answer so am posting as a reply to Tato here as his plugin works well for me and is exactly what I needed but has a bug (I use it as a dependency so the map
variable is passed through the function)
我没有足够的声誉作为答案发布,所以我在这里发布作为对 Tato 的回复,因为他的插件对我来说效果很好,这正是我需要的,但有一个错误(我将它用作依赖项,因此map
传递了变量通过函数)
You need to pass map
to function getOptimalZoomOut(latLng, currentZoom) {}
你需要传递map
给function getOptimalZoomOut(latLng, currentZoom) {}
as you use the map
variable inside that function.
当您map
在该函数中使用变量时。
like this: function getOptimalZoomOut(latLng, currentZoom, map) {}
像这样: function getOptimalZoomOut(latLng, currentZoom, map) {}
and later: map.setZoom(getOptimalZoomOut(destLatLng, initialZoom));
pass it in: map.setZoom(getOptimalZoomOut(destLatLng, initialZoom, map));
and maybe another stray one.
后来:map.setZoom(getOptimalZoomOut(destLatLng, initialZoom));
传递它:map.setZoom(getOptimalZoomOut(destLatLng, initialZoom, map));
也许是另一个流浪者。