Google Maps JS API点击标记时默认路线折线不显示问题
问题描述
整合Google Map API的Directions服务与Places服务时,预期实现以下功能:
- 获取用户当前位置地理定位
- 搜索周边指定类型地点,示例代码中搜索关键词为
sushi - 在地图上为所有搜索结果创建对应标记(Marker)
- 点击单个标记时:
- 标记对应地点的核心信息展示在左侧侧边栏
- 地图渲染用户当前位置到被点击标记之间的默认蓝色路线折线(Polyline)
- 点击下一个标记时,路线自动刷新为当前位置到新标记的路径
当前故障:地图始终无法显示默认蓝色路线折线,控制台无报错,Directions API可正常返回响应,响应数据中包含默认蓝色折线的相关配置,但折线始终无法在地图上渲染。
原始问题代码
let pos; let map; let bounds; let infoWindow; let currentInfoWindow; let service; let infoPane; function initMap() { // Initialize variables directionsService = new google.maps.DirectionsService; directionsDisplay = new google.maps.DirectionsRenderer({ map: map }); bounds = new google.maps.LatLngBounds(); infoWindow = new google.maps.InfoWindow; currentInfoWindow = infoWindow; /* TODO: Step 4A3: Add a generic sidebar */ infoPane = document.getElementById('panel'); // Try HTML5 geolocation if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(position => { pos = { lat: position.coords.latitude, lng: position.coords.longitude }; map = new google.maps.Map(document.getElementById('map'), { center: pos, zoom: 15 }); bounds.extend(pos); infoWindow.setPosition(pos); infoWindow.setContent('Location found.'); infoWindow.open(map); map.setCenter(pos); // Call Places Nearby Search on user's location getNearbyPlaces(pos); }, () => { // Browser supports geolocation, but user has denied permission handleLocationError(true, infoWindow); }); } else { // Browser doesn't support geolocation handleLocationError(false, infoWindow); } } // Handle a geolocation error function handleLocationError(browserHasGeolocation, infoWindow) { // Set default location to Sydney, Australia pos = { lat: -33.856, lng: 151.215 }; map = new google.maps.Map(document.getElementById('map'), { center: pos, zoom: 15 }); // Display an InfoWindow at the map center infoWindow.setPosition(pos); infoWindow.setContent(browserHasGeolocation ? 'Geolocation permissions denied. Using default location.' : 'Error: Your browser doesn\'t support geolocation.'); infoWindow.open(map); currentInfoWindow = infoWindow; // Call Places Nearby Search on the default location getNearbyPlaces(pos); } // Perform a Places Nearby Search Request function getNearbyPlaces(position) { let request = { location: position, rankBy: google.maps.places.RankBy.DISTANCE, keyword: 'sushi' }; service = new google.maps.places.PlacesService(map); service.nearbySearch(request, nearbyCallback); } // Handle the results (up to 20) of the Nearby Search function nearbyCallback(results, status) { if (status == google.maps.places.PlacesServiceStatus.OK) { createMarkers(results); } } // Set markers at the location of each place result function createMarkers(places) { places.forEach(place => { let marker = new google.maps.Marker({ position: place.geometry.location, map: map, title: place.name }); /* TODO: Step 4B: Add click listeners to the markers */ // Add click listener to each marker google.maps.event.addListener(marker, 'click', () => { clicked = marker.getPosition(); let request = { placeId: place.place_id, fields: ['name', 'formatted_address', 'geometry', 'rating', 'website', 'photos'] }; calculateAndDisplayRoute(directionsService, directionsDisplay, pos, clicked); console.log(clicked); /* Only fetch the details of a place when the user clicks on a marker. * If we fetch the details for all place results as soon as we get * the search response, we will hit API rate limits. */ service.getDetails(request, (placeResult, status) => { showDetails(placeResult, marker, status) }); }); // Adjust the map bounds to include the location of this marker bounds.extend(place.geometry.location); }); /* Once all the markers have been placed, adjust the bounds of the map to * show all the markers within the visible area. */ map.fitBounds(bounds); } function calculateAndDisplayRoute(directionsService, directionsDisplay, pos, clicked) { directionsService.route({ origin: pos, destination: clicked, avoidTolls: true, avoidHighways: false, travelMode: google.maps.TravelMode.WALKING }, function(response, status) { console.log(response, status); if (status == google.maps.DirectionsStatus.OK) { directionsDisplay.setDirections(response); } else { window.alert('Directions request failed due to ' + status); } }); } /* TODO: Step 4C: Show place details in an info window */ // Builds an InfoWindow to display details above the marker function showDetails(placeResult, marker, status) { if (status == google.maps.places.PlacesServiceStatus.OK) { let placeInfowindow = new google.maps.InfoWindow(); let rating = "None"; if (placeResult.rating) rating = placeResult.rating; placeInfowindow.setContent('<div><strong>' + placeResult.name + '</strong><br>' + 'Rating: ' + rating + '</div>'); placeInfowindow.open(marker.map, marker); currentInfoWindow.close(); currentInfoWindow = placeInfowindow; showPanel(placeResult); } else { console.log('showDetails failed: ' + status); } } /* TODO: Step 4D: Load place details in a sidebar */ // Displays place details in a sidebar function showPanel(placeResult) { // If infoPane is already open, close it if (infoPane.classList.contains("open")) { infoPane.classList.remove("open"); } // Clear the previous details while (infoPane.lastChild) { infoPane.removeChild(infoPane.lastChild); } /* TODO: Step 4E: Display a Place Photo with the Place Details */ // Add the primary photo, if there is one if (placeResult.photos) { let firstPhoto = placeResult.photos[0]; let photo = document.createElement('img'); photo.classList.add('hero'); photo.src = firstPhoto.getUrl(); infoPane.appendChild(photo); } // Add place details with text formatting let name = document.createElement('h1'); name.classList.add('place'); name.textContent = placeResult.name; infoPane.appendChild(name); if (placeResult.rating) { let rating = document.createElement('p'); rating.classList.add('details'); rating.textContent = `Rating: ${placeResult.rating} \u272e`; infoPane.appendChild(rating); } let address = document.createElement('p'); address.classList.add('details'); address.textContent = placeResult.formatted_address; infoPane.appendChild(address); if (placeResult.website) { let websitePara = document.createElement('p'); let websiteLink = document.createElement('a'); let websiteUrl = document.createTextNode(placeResult.website); websiteLink.appendChild(websiteUrl); websiteLink.title = placeResult.website; websiteLink.href = placeResult.website; websitePara.appendChild(websiteLink); infoPane.appendChild(websitePara); } // Open the infoPane infoPane.classList.add("open"); }
故障原因
DirectionsRenderer初始化时机错误:代码在initMap函数开头就初始化directionsDisplay并传入map参数,但此时map变量还未被赋值(map实例是在后续地理定位成功/失败的回调中才创建的),因此DirectionsRenderer实际绑定的是undefined空对象,后续即使拿到正确的路线响应,也无法渲染到实际生成的地图实例上。
修复方案
- 提前声明路线服务和渲染器变量,不要在map实例创建前初始化
- 在两个分支(定位成功/定位失败走默认位置)的map实例创建完成后,再初始化
directionsService和directionsDisplay,确保渲染器绑定到真实存在的map对象上 - 构造函数调用补充括号,符合JS编码规范
修正后的核心代码
initMap 函数修改
let pos; let map; let bounds; let infoWindow; let currentInfoWindow; let service; let infoPane; // 提前声明路线相关变量 let directionsService; let directionsDisplay; function initMap() { bounds = new google.maps.LatLngBounds(); infoWindow = new google.maps.InfoWindow(); currentInfoWindow = infoWindow; infoPane = document.getElementById('panel'); if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(position => { pos = { lat: position.coords.latitude, lng: position.coords.longitude }; map = new google.maps.Map(document.getElementById('map'), { center: pos, zoom: 15 }); // map实例创建完成后再初始化路线相关对象 directionsService = new google.maps.DirectionsService(); directionsDisplay = new google.maps.DirectionsRenderer({ map: map }); bounds.extend(pos); infoWindow.setPosition(pos); infoWindow.setContent('定位成功'); infoWindow.open(map); map.setCenter(pos); getNearbyPlaces(pos); }, () => { handleLocationError(true, infoWindow); }); } else { handleLocationError(false, infoWindow); } }
handleLocationError 函数修改
function handleLocationError(browserHasGeolocation, infoWindow) { pos = { lat: -33.856, lng: 151.215 }; map = new google.maps.Map(document.getElementById('map'), { center: pos, zoom: 15 }); // 默认位置分支创建map后同步初始化路线对象 directionsService = new google.maps.DirectionsService(); directionsDisplay = new google.maps.DirectionsRenderer({ map: map }); infoWindow.setPosition(pos); infoWindow.setContent(browserHasGeolocation ? '定位权限被拒绝,使用默认位置' : '错误:当前浏览器不支持地理定位'); infoWindow.open(map); currentInfoWindow = infoWindow; getNearbyPlaces(pos); }
其余业务逻辑无需修改,调整后点击标记即可正常渲染蓝色步行路线,切换标记时路线会自动刷新。
内容的提问来源于stack exchange,提问作者Sam Gu
相关产品推荐
相关产品推荐

