谷歌地图v3 - 我可以确保每次平稳平移吗?

时间:2010-09-28 23:41:41

标签: javascript google-maps google-maps-api-3

我的地图在一个城市内有几百个标记。通常不超过20英里半径。 我已经阅读了文档并且没有找到一种方法来设置init以在每个标记之间自动平移,无论距离如何。 默认行为是平移,如果关闭,跳转到远。 我明白为什么他们会这样做,因为地图不会在选定的缩放级别加载整个世界,如果距离太大,它可能会搞砸。但是,我认为它可以在最小的投诉下处理20英里半径。

如果有人有任何想法,我很乐意听到他们的意见。 感谢

6 个答案:

答案 0 :(得分:21)

平滑平移的阈值不依赖于当前中心与新目标之间的距离。这取决于更改是否需要整页滚动(水平和垂直):

引用API Reference

  

<强>哑剧(经纬度:经纬度)

     

将地图中心更改为给定的LatLng。如果更改小于地图的宽度和高度,则过渡将平滑地动画。

因此,只要缩小您的视口高度和宽度为20英里,就应保证在20英里以下的距离内进行平滑平移。

答案 1 :(得分:12)

这是一个平稳平移的解决方案,并允许其他点击请求在前一个平移已经进行时排队:

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

答案 2 :(得分:4)

请参阅此其他有关使用javascript的setInterval函数创建在地图上调用panBy的定期函数的答案:Can Google Maps be set to a slow constant pan? Like a globe revolution?

这可用于在每次调用panBy时按x像素平移地图,允许你减慢panBy速率(因为你只是告诉gmaps panTo一小段距离)。

答案 3 :(得分:1)

正如丹尼尔所提到的,如果两点相距太远,内置的panTo()函数将不适合你。如果是这种情况,您可以手动为其自动设置动画:对于每个缩放级别,计算出100像素所覆盖的距离。现在,当你必须平移到一个点时,你可以使用这些信息来确定panTo()函数是否会动画或跳转。如果移动的距离太大而无法设置动画,则应手动执行动画 - 在当前地图中心和目的地之间计算一些中间航点,并按顺序平移它们。

答案 4 :(得分:1)

我们开发了一种变通方法,可以在所有情况下为panTo设置流畅的动画。

基本上,如果原生panTo的动画效果不佳,我们zoom outpanTozoom in到目标位置。

要使用下面的代码,请调用smoothlyAnimatePanTo,并将map实例作为第一个参数,并将目标latLng作为第二个参数。

有一个jsfiddle在操作here中演示此解决方案。只需编辑script标签以放置您自己的Google Maps JavaScript API键即可。

任何评论和贡献都将受到欢迎。

/**
 * 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(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(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(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)
    }
}

答案 5 :(得分:0)

@ tato.rodrigo

我没有足够的声誉来发布答案,所以在这里发布对Tato的回复,因为他的插件对我来说效果很好,正是我所需要的,但是有一个bug(我将其用作依赖项,因此map变量通过函数传递)

您需要将map传递给function getOptimalZoomOut(latLng, currentZoom) {}

在该函数中使用map变量时。

像这样:function getOptimalZoomOut(latLng, currentZoom, map) {}

及更高版本:map.setZoom(getOptimalZoomOut(destLatLng, initialZoom));传递给它:map.setZoom(getOptimalZoomOut(destLatLng, initialZoom, map));,也许还有另一个流浪者。