如何扩展 Google 地图标记以平滑地进行动画更新?

32

使用Google Maps API v3,我可以通过AJAX调用更新多个标记的位置。然而,它缺乏任何过渡效果。以下是代码:

if ( !latlong.equals( point.latlong ) ) {
    point.latlong = latlong;
    point.marker.setPosition(latlong);
}

缺点是setPosition没有本机动画方法。 有人知道扩展setPosition的任何方法,以便标记可以从其旧位置流畅地“移动”到新位置吗? 或者是否有任何可用的方法? 我找不到任何文档。 谢谢!

3个回答

59

我没有找到任何原生的方法来创建这个动画。你可以通过使用setPosition,从当前点到最终点逐步设置位置来创建自己的动画。以下是一个代码片段,以便为您提供一个思路:

var map = undefined;
var marker = undefined;
var position = [43, -89];

function initialize() {

    var latlng = new google.maps.LatLng(position[0], position[1]);
    var myOptions = {
        zoom: 8,
        center: latlng,
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);

    marker = new google.maps.Marker({
        position: latlng,
        map: map,
        title: "Your current location!"
    });

    google.maps.event.addListener(map, 'click', function(me) {
        var result = [me.latLng.lat(), me.latLng.lng()];
        transition(result);
    });
}

var numDeltas = 100;
var delay = 10; //milliseconds
var i = 0;
var deltaLat;
var deltaLng;
function transition(result){
    i = 0;
    deltaLat = (result[0] - position[0])/numDeltas;
    deltaLng = (result[1] - position[1])/numDeltas;
    moveMarker();
}

function moveMarker(){
    position[0] += deltaLat;
    position[1] += deltaLng;
    var latlng = new google.maps.LatLng(position[0], position[1]);
    marker.setPosition(latlng);
    if(i!=numDeltas){
        i++;
        setTimeout(moveMarker, delay);
    }
}
这段代码可能需要进行一些清理,但是可以让你好好开始。我正在使用JavaScript的setTimeout方法创建动画效果。对'transition'的初始调用启动了动画效果。传递给'transition'的参数是一个二元素数组[lat, lng]。'transition'函数根据几个动画参数(numDeltas、delay)计算出纬度和经度的步长,然后调用'moveMarker'函数。'moveMarker'函数保持简单的计数器以表示标记已到达最终目的地的情况。如果还没有到达,它会再次调用自身。
这里有一个工作示例的jsFiddle代码:https://jsfiddle.net/rcravens/RFHKd/2363/ 希望这能有所帮助。
Bob

4
好的解决方案!我修改了您的代码,使其能够添加特定的速度单位(公里/小时):http://jsfiddle.net/pmrotule/9tfq5sqc/8/ - pmrotule
@pmrotule 太棒了。我试图在标记上显示动态标签,但可能由于它不断更改标记,因此未显示。有什么建议吗? - User-8017771
@User-8017771 不是很确定,我需要看一下你的代码。我猜当你改变位置时它会隐藏标签。为了完全控制标记的外观,你可以使用类似这样的东西 https://github.com/aaronmiler/beer-map/blob/master/app/assets/javascripts/custom_marker.js - pmrotule
@pmrotule 我没有改变您的代码太多。仅为测试,我在您的jsfiddle中添加了标签作为纬度。 第72和73行: label:startPos [0], title:startPos [0] 也许需要做其他事情吗? - User-8017771
@pmrotule 还有一件需要考虑的事情是:如果标签是静态的,它会被显示。但是当我尝试使其动态时,它根本不会被显示。有什么想法吗? - User-8017771
1
@User-8017771 我不明白为什么你要把标签设置为纬度... labelgoogle.maps.Marker 的一个选项,所以你可以在创建标记时设置它,或者使用 .setOptions() 在以后更新它。例如,在我的 fiddle 中,你可以在 moveMarker() 函数的末尾添加 marker.setOptions({ label: lat.toString() });。如果你需要更多帮助,请在 Stackoverflow 上创建一个新问题。 - pmrotule


8

虽然有些晚了,但这可能会帮助未来的SO漫游者。
问题陈述:编写一个函数(而不是库,因为具体使用情况)以将谷歌地图标记动画移动到新位置。
解决方案基于这个神奇的库marker-animate-unobtrusive

function animateMarkerTo(marker, newPosition) {
    var options = {
        duration: 1000,
        easing: function (x, t, b, c, d) { // jquery animation: swing (easeOutQuad)
            return -c *(t/=d)*(t-2) + b;
        }
    };

    window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
    window.cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame;

    // save current position. prefixed to avoid name collisions. separate for lat/lng to avoid calling lat()/lng() in every frame
    marker.AT_startPosition_lat = marker.getPosition().lat();
    marker.AT_startPosition_lng = marker.getPosition().lng();
    var newPosition_lat = newPosition.lat();
    var newPosition_lng = newPosition.lng();

    // crossing the 180° meridian and going the long way around the earth?
    if (Math.abs(newPosition_lng - marker.AT_startPosition_lng) > 180) {
        if (newPosition_lng > marker.AT_startPosition_lng) {
            newPosition_lng -= 360;
        } else {
            newPosition_lng += 360;
        }
    }

    var animateStep = function(marker, startTime) {
        var ellapsedTime = (new Date()).getTime() - startTime;
        var durationRatio = ellapsedTime / options.duration; // 0 - 1
        var easingDurationRatio = options.easing(durationRatio, ellapsedTime, 0, 1, options.duration);

        if (durationRatio < 1) {
            marker.setPosition({
                lat: (
                    marker.AT_startPosition_lat +
                    (newPosition_lat - marker.AT_startPosition_lat)*easingDurationRatio
                ),
                lng: (
                    marker.AT_startPosition_lng +
                    (newPosition_lng - marker.AT_startPosition_lng)*easingDurationRatio
                )
            });

            // use requestAnimationFrame if it exists on this browser. If not, use setTimeout with ~60 fps
            if (window.requestAnimationFrame) {
                marker.AT_animationHandler = window.requestAnimationFrame(function() {animateStep(marker, startTime)});
            } else {
                marker.AT_animationHandler = setTimeout(function() {animateStep(marker, startTime)}, 17);
            }

        } else {
            marker.setPosition(newPosition);
        }
    }

    // stop possibly running animation
    if (window.cancelAnimationFrame) {
        window.cancelAnimationFrame(marker.AT_animationHandler);
    } else {
        clearTimeout(marker.AT_animationHandler);
    }

    animateStep(marker, (new Date()).getTime());
}

谢谢。它确实有效,但在我的实现中,它会留下幽灵标记。我该如何解决这个问题? - Mr Smith
1
你能提供更多信息或者一个 Fiddle 吗? - mohitSehgal
感谢您的迅速回复。我正在一个Angular应用程序中使用此函数,其中坐标来自Firebase。问题在于,在前几次移动后,标记的图标会变形和缩小。标记移动平稳,但会留下一串“幽灵”标记。它也会在到达目的地之前出现。 - Mr Smith
我已经成功解决了这个问题。它与我如何更新已经在本地状态中的卡车坐标有关。感谢这个函数。 - Mr Smith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接