JS Google Maps API v3 在坐标之间实现标记动画

14
我正在开发一个简单的 JavaScript 地图应用程序,需要将多个标记点在不同坐标之间进行动画移动。每个标记点可以自由移动,所有标记点都存储在数组列表中。但是,我一直无法使它们平稳地过渡到新位置。我已经做了大量的研究和试错,但没有成功,有人能帮助吗?
4个回答

23

我的快速且不太严谨的方法并没有涉及大量的研究 :(

这是演示: http://jsfiddle.net/yV6xv/4/ 点击标记以开始移动,停止后再次点击即可回到初始点。在运动过程中点击会产生奇怪的结果。

起点和终点在initialize()中预定义。动画是通过将起点和终点分成100段,并在这些点上设置一个固定的时间间隔来定义的。因此,动画时间是固定的:标记行驶的距离越长,“速度”就越快。

我没有进行太多测试,知道在移动的标记上单击会产生意外的结果(起点和终点会错位)。

这是演示的“有趣”部分:

      // store a LatLng for each step of the animation
      frames = [];
      for (var percent = 0; percent < 1; percent += 0.01) {
        curLat = fromLat + percent * (toLat - fromLat);
        curLng = fromLng + percent * (toLng - fromLng);
        frames.push(new google.maps.LatLng(curLat, curLng));
      }

      move = function(marker, latlngs, index, wait, newDestination) {
        marker.setPosition(latlngs[index]);
        if(index != latlngs.length-1) {
          // call the next "frame" of the animation
          setTimeout(function() { 
            move(marker, latlngs, index+1, wait, newDestination); 
          }, wait);
        }
        else {
          // assign new route
          marker.position = marker.destination;
          marker.destination = newDestination;
        }
      }

      // begin animation, send back to origin after completion
      move(marker, frames, 0, 20, marker.position);

6
谢谢!作为一个刚接触JS的人来说,这篇文章很容易理解(意思是:对于初学者来说仍然很难),帮助了我很多。我最初fork了它并把它变成了新手的混乱,但我又回到了你的原始版本,并重新fork了它,以包括一个循环和每个标记不同的速度。再次感谢Tina。 - Devin M

20
你可以使用 marker-animate-unobtrusive 库来让标记平滑地从一个位置过渡到另一个位置。
你可以像这样初始化你的标记:
var marker = new SlidingMarker({
   //your original marker options
   //...
   duration: 1000
});

通过这个设置,您的标记将在1秒内平滑移动到新位置,只需调用marker.setPosition()。

如果您想要让标记来回移动,只需每秒切换一次setPosition即可。

setTimeout(function() {
   var newPosition = /* select new position */
   marker.setPosition(newPosition)
}, 1000);

附言:我是该库的作者。


工作得非常好。能够在不更改任何代码的情况下实现结果。 - Farveaz
1
这就是“不显眼”的理念,很高兴听到它对你有用。 - viskin
注意:如果您使用overlayviews,似乎无法正常工作。 - Barry Chapman

14
我不确定这是否是您要找的,但我还是会分享一下:我编写了这段代码来模拟以特定时速(公里/小时)汽车的移动。您只需要指定您想让标记/汽车前往的每个点的坐标即可(然后它将在这些坐标之间播放动画)。
我修改了rcravens的答案以得到这个结果:
var map, marker;
var startPos = [42.42679066670903, -83.29210638999939];
var speed = 50; // km/h

var delay = 100;
// If you set the delay below 1000ms and you go to another tab,
// the setTimeout function will wait to be the active tab again
// before running the code.
// See documentation :
// https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout#Inactive_tabs

function animateMarker(marker, coords, km_h)
{
    var target = 0;
    var km_h = km_h || 50;
    coords.push([startPos[0], startPos[1]]);

    function goToPoint()
    {
        var lat = marker.position.lat();
        var lng = marker.position.lng();
        var step = (km_h * 1000 * delay) / 3600000; // in meters

        var dest = new google.maps.LatLng(
        coords[target][0], coords[target][2]);

        var distance =
        google.maps.geometry.spherical.computeDistanceBetween(
        dest, marker.position); // in meters

        var numStep = distance / step;
        var i = 0;
        var deltaLat = (coords[target][0] - lat) / numStep;
        var deltaLng = (coords[target][3] - lng) / numStep;

        function moveMarker()
        {
            lat += deltaLat;
            lng += deltaLng;
            i += step;

            if (i < distance)
            {
                marker.setPosition(new google.maps.LatLng(lat, lng));
                setTimeout(moveMarker, delay);
            }
            else
            {   marker.setPosition(dest);
                target++;
                if (target == coords.length){ target = 0; }

                setTimeout(goToPoint, delay);
            }
        }
        moveMarker();
    }
    goToPoint();
}

function initialize()
{
    var myOptions = {
        zoom: 16,
        center: new google.maps.LatLng(42.425175091823974, -83.2943058013916),
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);

    marker = new google.maps.Marker({
        position: new google.maps.LatLng(startPos[0], startPos[1]),
        map: map
    });

    google.maps.event.addListenerOnce(map, 'idle', function()
    {
        animateMarker(marker, [
            // The coordinates of each point you want the marker to go to.
            // You don't need to specify the starting position again.
            [42.42666395645802, -83.29694509506226],
            [42.42300508749226, -83.29679489135742],
            [42.42304468678425, -83.29434871673584],
            [42.424882066428424, -83.2944130897522],
            [42.42495334300206, -83.29203128814697]
        ], speed);
    });
}

initialize();

jsfiddle - 演示

请注意,在包含Google地图时,您需要添加“geometry”库才能使用google.maps.geometry.spherical.computeDistanceBetweenhttp://maps.google.com/maps/api/js?sensor=true&libraries=geometry

希望这有所帮助!


如何使“animateMarker”方法的“coords”参数成为动态的?在我的情况下,我应该从一个位置(lat,lang)开始。嵌套点(lat,lang)应该从数据库中获取。 - Chandan Kumar
@ChandanKumar 坐标是动态的,标记将从一个坐标点动画到下一个坐标点。如果您想获取从一个点到另一个点的方向,请使用 https://developers.google.com/maps/documentation/javascript/directions 并在响应中查看 response.routes [0] .overview_path,这相当于此 animateMarker 方法中的 coords。希望能有所帮助。 - pmrotule

-1

另一种选择是使用CSS过渡。重要的部分是识别Google Maps用于标记的DIV(有2个,其中一个是用于触摸事件的透明)。这项调查已经为您完成,您只需要理解一次即可。

完整的示例可以在此处找到。看看汉赛尔和格雷特尔如何在地图上平稳移动!如果有任何延迟,过渡时间将合并。

我的Brotkrumen Ultimate Web App的所有代码都可以在此处找到,您可能最感兴趣的是HandleMap.js文件,但还有一个aaa_readme.txt。

以下是代码的一部分:-

function showJourney(){
    map.setZoom(map.getZoom());
    map.setOptions({gestureHandling: "none"});
    zoomOut.style.display = "none";
    zoomIn.style.display  = "none";

    hat.setPosition(
        new google.maps.LatLng(
                lastPos.coords.latitude,
                lastPos.coords.longitude)); 
    hat.setVisible(true);
    hat.setAnimation(bounce);

    HandG.setPosition(
        new google.maps.LatLng(
                firstPos.coords.latitude,
                firstPos.coords.longitude)); 
    HandG.setVisible(true);

    map.panTo(path[0]); 
    google.maps.event.trigger(map, 'resize');

    if (document.querySelectorAll(MARKER_SELECTOR).length == 0){
        observer.observe(mapDiv, {
                        childList     : true,
                        subtree       : true ,
                        attributes    : true ,
                        characterData : false
                        })
    } else {
        setTimeout(plotTrip,0);
    }
}
function plotTrip(){
    nextFunc = plotStep;
    hat.setAnimation(bounce);
    HandG.setPosition(path[0]);
    dirPoly.setVisible(true);       
    progressPath = [];
    progressPath.push(path[0]);
    dirPoly.setPath(path);
    stepPoly.setPath(progressPath);
    stepPoly.setVisible(true);
    currStep = 1;
    markerDivs = [];
    var markerImgs = document.querySelectorAll(MARKER_SELECTOR);
    for (var i=0; i<markerImgs.length; i++){
        console.log(markerImgs[i].src);
        markerDivs[i] = markerImgs[i].parentNode;
        markerDivs[i].style.transitionDuration = "0s";
        markerDivs[i].style.transitionProperty = "left, top";
        markerDivs[i].style.transitionTimingFunction = "linear";
    }

    setTimeout(plotStep,0);
    abort = false;
    btn.value = "Cancel";
    btn.disabled = false;
}
function plotStep(){
    if (abort) return;

    if (legs[currStep].didLoiter){
        countDown = legs[currStep].restTime;
        infoWindow.setContent(
            "<div id='waitDiv'><span>Waiting</span></div>");
        infoWindow.open(map,HandG);
        showInterval();
    } else {
        plotIt();
    }
}
function showInterval(){
    if (abort) return;

    infoWindow.setContent(
        "<div id='waitDiv'><span>Waiting "+deltaDate(countDown)+"</span></div>");
    countDown -= (ONE_SEC * multiSpeed);
    if (countDown < 1){
        infoWindow.close(); 
        plotIt();
    } else {
        setTimeout(showInterval, ONE_SEC);
    }
}
function plotIt(){
    if (abort) return;

    progressPath.push(path[currStep]);
    stepPoly.setPath(progressPath);
    map.panTo(path[currStep]);
    var transitionMS = legs[currStep].duration / multiSpeed;
    for (var i=0; i<markerDivs.length; i++){
        markerDivs[i].style.transitionDuration = transitionMS + "ms";
    }
    HandG.setPosition(path[currStep])

    if (++currStep >= path.length)
        nextFunc = cleanUp;

    plotTimer = setTimeout(nextFunc,transitionMS);
}
function cleanUp(){
    infoWindow.close();
    hat.setAnimation();
    btn.value = "Replay";
    btn.disabled = false;
    clearTimeout(plotTimer);
    for (var i=0; i<markerDivs.length; i++){
        markerDivs[i].style.transitionDuration = "0s";
    }
    HandG.setPosition(
        new google.maps.LatLng(
                lastPos.coords.latitude,
                lastPos.coords.longitude)); 
    HandG.setVisible(false);
    map.setOptions({gestureHandling: "cooperative"});
    zoomIn.style.display  = "";
    zoomOut.style.display = "";
    if (canTalk && !abort)
        speechSynthesis.speak(finish);
}
function waitForMarker(mutations, myInstance) {
    outer:
    for (var i=0; i<mutations.length; i++){
        if (mutations[i].type           == "attributes" && 
            mutations[i].target.tagName == "IMG"        &&
            mutations[i].target.src.toLowerCase().indexOf(MARKER_SRC) != -1){
            console.log("result")
            myInstance.disconnect();
            setTimeout(plotTrip,0)
            break outer;
        }
        if (mutations[i].type != "childList" ||
            mutations[i].addedNodes.length   == 0) 
            continue;
        for (var j=0; j<mutations[i].addedNodes.length; j++) {
            var node = mutations[i].addedNodes[j];
            if (node.tagName == "DIV" && node.firstChild && node.firstChild.tagName == "IMG" &&
                node.firstChild.src.toLowerCase().indexOf(MARKER_SRC) != -1){
                console.log(node.firstChild.src);
                myInstance.disconnect();
                setTimeout(plotTrip,0)
                break outer;
            }
        }
    }
} 

1
链接已经失效。 - omair azam
1
链接现在已经修复。 - McMurphy

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