可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a simple javascript maps application that I'm working on that requires me to animate the movement of multiple markers between different coords. Each marker is free to move on its own and all markers are stored in an array list. However, I have been having trouble getting them to smoothly transition locations.
I've done a ton of research and trial/error but no luck, anyone have any luck with this?
回答1:
My quick-and-dirty approach does not involve a ton of research :(
Here's the demo: http://jsfiddle.net/yV6xv/4/ Click on a marker to begin moving it, after it stops, you can click again to go back to its initial point. Clicking while in motion gives strange results.
Start and endpoints are predefined in initialize()
. The animation is defined by dividing the start and endpoints into 100 segments, and placing the marker at these points with a set interval. So the animation time is fixed: markers travel longer distances "faster" than shorter distances.
I didn't do much testing, I know clicking on a moving marker will give unexpected results (start and endpoints get misplaced)
This is the "interesting" part of the demo:
// 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);
回答2:
You can use marker-animate-unobtrusive library to make markers
smoothly transition from one location to another.
You could initialize your marker like that:
var marker = new SlidingMarker({
//your original marker options
//...
duration: 1000
});
With this defined, your marker will smoothly move to a new position within 1 second, just call marker.setPosition().
If you want to animate marker back-and-forth, just toggle setPosition each second.
setTimeout(function() {
var newPosition = /* select new position */
marker.setPosition(newPosition)
}, 1000);
P.S. I'm the author of the library.
回答3:
I'm not sure if it is what you are looking for but I will share it anyway : I wrote this code to simulate the movement of a car with a specific speed in km/h. You just need to specify the coordinates of each point you want the marker/car to go to (then it will animate the marker between the coordinates).
I modified rcravens's answer to get to this:
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 - DEMO
Note that you need to add the "geometry" library when you include google maps to be able to use google.maps.geometry.spherical.computeDistanceBetween
:
http://maps.google.com/maps/api/js?sensor=true&libraries=geometry
Hope it helps!
回答4:
An alternative is to use CSS transitions. The important bit is identifying the DIVs Google Maps is using for your marker (There are 2 one is transparent for touch events) The investigation has been done for you and you really only have to understand it once.
A complete example can be found here See how smoothly Hansel and Gretel move around the map! And the transition times coalesce if there is any delay.
All the code for my Brotkrumen Ultimate Web App can be found here you will be mostly interested in the HandleMap.js file but there is a aaa_readme.txt
Here is part of the code: -
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;
}
}
}
}