Google Maps API JavaScript:Rover标记仅移动到最后航点问题排查
问题描述
我基于Google Maps API JavaScript开发了一款Rover导航面板,功能流程如下:在地图上定位Rover(旗帜图标),添加模拟航点的标记;点击START按钮后,Rover需依次移动到每个航点,最终停在最后一个。但当前问题是,Rover仅会移动到最后一个添加的标记。我使用动态数组存储标记,原计划通过For循环结合setPosition()方法,让Rover先抵达第一个标记,等待5秒(setTimeout(5000))后再移动到下一个航点。请问我的代码存在什么问题?
JavaScript代码
/** * @license * Copyright 2019 Google LLC. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ //MAP--------------------------------- function initMap() { //array arr1 which will contain the dynamically added markers var arr1=[]; //myLatLng object with lat and lng attributes for position (europe) let myLatLng = { lat: 44.525961, lng: 15.255119 }; //constant map and assigned the map placed in the div id="map" const map = new google.maps.Map(document.getElementById("map"), { zoom: 4, //coordinates europe center: myLatLng, }); //MARKER-------------------------------------- //marker object to put starting marker (rover) var markerRover = new google.maps.Marker({ position: myLatLng, map: map, draggable: true, title: "ROVER", icon: "https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png" }); //variables to write the rover coordinates in form input let latitudine= document.getElementById('latitudine').value= markerRover.position.lat(); let longitudine= document.getElementById('longitudine').value= markerRover.position.lng(); //CLICK EVENT TO ADD MARKER by clicking on the map, calls the placeMarker function which places the marker on the map where the click occurred map.addListener("click", (e) => { placeMarker(e.latLng, map); }); function placeMarker(latLng, map) { var mark= new google.maps.Marker({ position: latLng, map: map, }); // dynamic array to store the markers placed by placeMarker arr1.push(mark); //test print arr dynamic markers console.log(arr1); //event for START button var START = document.getElementById('start'); START.addEventListener("click", function spostaRover() { //test print to see the length of the array console.log(arr1.length); //for loop to set rover position on markers added dynamically for(let i=0; i<arr1.length; i++) { markerRover.setPosition(arr1[i].position); //attempt to wait 5s and then move rover marker to new position setTimeout(() => markerRover.setPosition(arr1[i].position),5000); console.log('marcatore '+i+' fatto') //in the Rover Coordinates input form, I add the new coordinates that the rover will have latitudine=document.getElementById('latitudine').value= markerRover.position.lat(); longitudine=document.getElementById('longitudine').value= markerRover.position.lng(); } }); /* //evento per tasto CONFIRM var CONFIRM = document.getElementById('confermaTappa'); CONFIRM.addEventListener("click", function addTappa(){ // scrive in textArea le coordinate dell'ultimo marcatore creato let td1= document.getElementById("coordTry").textContent= "latitudine: "+mark.position.lat()+"\n" +"longitudine: " +mark.position.lng(); //let td2= document.getElementById("coordTry").textContent=mark.position.lng(); }); //evento per tasto remove var REMOVE = document.getElementById('removeMarker'); REMOVE.addEventListener("click", function removeMarker(){ //leva da arr1 (array dei marcatori) ultimo marcatore da array arr1.pop(mark); //prova di stampa console.log(arr1); mark.setMap(null); //manca di togliere ultimo marcatore dalla mappa }); */ }//closing placemarker } window.initMap = initMap;
HTML代码
<!DOCTYPE html> <!-- @license Copyright 2019 Google LLC. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 --> <html> <head> <title>geo-rover</title> <script src="script.js"></script> <link rel="stylesheet" href="style.css"> <!-- jsFiddle will insert css and js --> </head> <body> <div class="container1"> <h1>GEO-ROVER</h1> <div id="map"> </div> <div class="containerPulsanti1"> <div class="containerPulsanti2"> <button class="pulsante" id="removeMarker" type="button"> REMOVE </button> <button class="pulsante" id="stop" type="button"> STOP </button> <button class="pulsante" id="start" type="button"> START </button> <button class="pulsante" id="confermaTappa" type="button"> CONFIRM </button> </div> </div> <!-- Container 2 per coordinate attuali e tabella tappe*/ --> <div class="container2"> <!-- Container 3 per gestire in flex contenuto container2 --> <div class="container3"> <!-- Container per label coordinate--> <div class="containerCoordinate"> <h2>Coordinate rover</h2> <div id="coordinate"> <label for="coordinate" class="coordinateLat" > Latitudine</label> <input type="text" class="coordinateGeo" id="latitudine" name="latitudine" disabled > <label for="coordinate" class="coordinateLong"> Longitudine</label> <input type="text" class="coordinateGeo" id="longitudine" name="longitudine" disabled > </div> </div> <div class="containerTable"> <h2>Coordinate tappe Rover</h2> <label for="coordTry"></label> <textarea id="coordTry" name="story" rows="10" cols="45" > </textarea> <!-- <table class="table"> <thead> <tr> <th>TAPPE</th> <th>LATITUDINE</th> <th>LONGITUDINE</th> </tr> </thead> <tbody class="bodyTable"> <tr> <th>1</th> <td id="tappa1lat">-</td> <td id="tappa1lng">-</td> </tr> <tr> <th>2</th> <td id="tappa2lat">--</td> <td id="tappa2lng">--</td> </tr> <tr> <th>3</th> <td> </td> <td> </td> </tr> <tr> <th>4</th> <td> </td> <td> </td> </tr> </tbody> </table> </div> --> </div> </div> </div> <!-- The `defer` attribute causes the callback to execute after the full HTML document has been parsed. For non-blocking uses, avoiding race conditions, and consistent behavior across browsers, consider loading using Promises with https://www.npmjs.com/package/@googlemaps/js-api-loader. --> <!--<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAFkjhEDLfud-pZFUtE_wdfspot2IWnuXI&callback=initMap&libraries=drawing&v=weekly" defer> </script> --> <!-- ,geometry,places ---non mio--> <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initMap&libraries=drawing&v=weekly" defer> </script> </body> </html>
CSS样式
/* stile html e body*/ @import url('https://fonts.googleapis.com/css?family=Coda'); html, body { background: rgb(0,0,0); background: linear-gradient(180deg, rgba(0,0,0,1) 71%, rgba(168,167,0,1) 100%, rgba(204,209,0,1) 100%); margin: 0; padding: 0; height: 150vh; width: 100% } /*my rules*/ /*TITOLI*/ h1{ font-family: Coda, cursive; font-style: italic; text-align: center; font-size: 35px; letter-spacing: 4px; word-spacing: 3px; color: rgb(247, 255, 0); } h2 { font-family: Coda, cursive; font-style: italic; text-align: center; letter-spacing: 4px; word-spacing: 3px; color: rgb(247, 255, 0); } /*container Principale che parte da sotto al titolo h1*/ .container1 { display: flex; flex-direction: column; height: 100%; } /*mappa GOOGLE div per contenere mappa*/ #map { flex: 6; } /*Container 2 per coordinate attuali e tabella tappe*/ .container2 { flex: 3; padding-bottom: 1%; } /*container 3 per gestire in flexbox contenuto container 2*/ .container3{ display: flex; flex-direction: row; justify-content: space-around; } .containerPulsanti1{ padding: 10px; flex:1; } .containerPulsanti2 { padding: 10px; margin: 10px; display: flex; flex-direction: row; justify-content: space-evenly; align-items: center; } .pulsante { cursor:pointer; color:#ffffff; font-family:Arial; font-size:17px ; padding:10px 25px; text-decoration:none; } .containerPulsanti2 { position:relative; top:1px; } #start{ box-shadow: 0px 0px 5px 0px #3dc21b; background-color:#44c767; border-radius:28px; border:1px solid #18ab29; } #start:hover { background-color:#5cbf2a; } #start:active { background-color:#055509; } #stop{ box-shadow: 0px 0px 5px 0px red; background-color:red; border-radius:28px; border:1px solid red; } #stop:hover { background-color: #f16161; } #stop:active { background-color:#890303; } #removeMarker{ color: #000000; box-shadow: 0px 0px 5px 0px yellow; background-color:yellow; border-radius:28px; border:1px solid yellow; } #removeMarker:hover { background-color:#fcf288; } #removeMarker:active { background-color:#7b7100; } #confermaTappa{ color: #000000; box-shadow: 0px 0px 5px 0px yellow; background-color:yellow; border-radius:28px; border:1px solid yellow; } #confermaTappa:hover { background-color:#fcf288; } #confermaTappa:active { background-color:#7b7100; } .coordinateGeo { padding: 5px; font-size: 16px; border-width: 2px; border-color: #fffb09; background-color: black; color: #ffffff; border-style: solid; border-radius: 10px; margin-top: 1vh; } /*.coordinateGeo:focus { outline:none; }*/ #coordinate { display: flex; flex-direction: column; text-align: center; } .coordinateLong { margin-top: 2vh; font-family: verdana; text-align: center; font-size: 20px; color: rgb(255, 255, 255); } .coordinateLat { margin-top: 2vh; font-family: verdana; text-align: center; font-size: 20px; color: rgb(255, 255, 255); } #latitudine{ text-align: center; } #longitudine{ text-align: center; }
问题分析与修复方案
核心问题1:循环中setTimeout的闭包陷阱
for循环里的setTimeout会在循环完全结束后才执行,此时变量i已经变成了数组的最后一个索引值,所以所有延迟执行的setPosition都会指向最后一个航点。
核心问题2:同步代码直接覆盖位置
循环里直接执行markerRover.setPosition(arr1[i].position);会立刻把Rover的位置设置到当前循环的航点,循环瞬间跑完后,Rover已经被设置到最后一个航点,后续延迟执行的代码又会重复设置最后一个航点。
核心问题3:事件监听重复绑定
每次点击地图添加航点时,你都会给START按钮重新绑定一次点击事件,这会导致点击START时,之前添加的所有航点对应的移动逻辑都会重复执行,造成混乱。
修复后的核心代码示例
function initMap() { var arr1=[]; let myLatLng = { lat: 44.525961, lng: 15.255119 }; const map = new google.maps.Map(document.getElementById("map"), { zoom: 4, center: myLatLng, }); var markerRover = new google.maps.Marker({ position: myLatLng, map: map, draggable: true, title: "ROVER", icon: "https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png" }); // 更新初始坐标 function updateRoverCoords() { document.getElementById('latitudine').value = markerRover.position.lat(); document.getElementById('longitudine').value = markerRover.position.lng(); } updateRoverCoords(); // 把START事件监听移到外面,只绑定一次 var START = document.getElementById('start'); START.addEventListener("click", function spostaRover() { console.log(arr1.length); // 递归实现依次延迟移动 function moveToNext(index) { if (index >= arr1.length) return; // 设置当前航点位置 markerRover.setPosition(arr1[index].position); updateRoverCoords(); console.log('marcatore ' + index + ' fatto'); //
相关产品推荐
相关产品推荐

