在Google Maps API中添加多个HTML标记的堆叠问题排查
解决Google Maps自定义HTML标记堆叠及缩放动画问题
问题说明
我需要在Google Maps上添加多个可定制的HTML标记,要求:
- 支持CSS样式自定义,内部可嵌入文本
- 随地图缩放实现动态效果:根据缩放级别调整标记大小、切换为圆点或显示完整样式
期望实现的效果:
目前遇到的问题:所有标记堆叠在一起,无法正常显示各自位置
现有代码
JavaScript
function init() { var locations = [ ['500k', -33.890542, 151.274856, 1], ['1.3M', -33.923036, 151.259052, 2], ['2.3M', -34.028249, 151.157507, 3], ['750k', -33.800101, 151.287478, 4], ['200K', -33.950198, 151.259302, 5], ]; var map = new google.maps.Map(document.getElementById('mapdiv'), { zoom: 10, center: new google.maps.LatLng(-33.92, 151.25), mapTypeId: google.maps.MapTypeId.ROADMAP, }); var infowindow = new google.maps.InfoWindow(); for (i = 0; i < locations.length; i++) { var price = locations[i][0]; var latMarker = locations[i][1]; var lngMarker = locations[i][2]; function HTMLMarker(lat, lng) { this.lat = lat; this.lng = lng; this.pos = new google.maps.LatLng(lat, lng); } HTMLMarker.prototype = new google.maps.OverlayView(); HTMLMarker.prototype.onRemove = function() {} HTMLMarker.prototype.onAdd = function() { div = document.createElement('div'); div.className = "arrow_box d-flex"; div.innerHTML = "<div class='m-auto'>SR " + price + "</div>"; var panes = this.getPanes(); panes.overlayImage.appendChild(div); } HTMLMarker.prototype.draw = function() { var overlayProjection = this.getProjection(); var position = overlayProjection.fromLatLngToDivPixel(this.pos); var panes = this.getPanes(); panes.overlayImage.style.left = position.x + 'px'; panes.overlayImage.style.top = position.y - 30 + 'px'; } var htmlmarker = new HTMLMarker(latMarker, lngMarker); htmlmarker.setMap(map); } }
CSS
html,body { height: 100%; margin: 0; padding: 0; } #mapdiv { height: 100%; width: 100%; } #mapdiv2 { height: 100%; width: 100%; } div.arrow_box { font-size: 10px; width: 60px; height: 20px; background: blue; border-radius: 10px; margin: 10px -30px; color: white; cursor: pointer; overflow: hidden; } div.arrow_box img { min-width: 150px; width: auto; max-height: 100px; height: auto; border-radius: 5px; } .arrow_box:after { top: 30px; left: 0px; border: solid transparent; content: " "; height: 0; width: 0; position: absolute; pointer-events: none; border-color: rgba(136, 183, 213, 0); border-top-color: blue; border-width: 5px; margin-left: -5px; }
HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous"> <title>Map Test</title> </head> <style></style> <body class="bg-dark d-flex p-1" onload="init()"> <div class="border border-primary bg-dark rounded" id="mapdiv"> </div> </body> <script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN" crossorigin="anonymous"></script> <script src='https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js'></script> </html>
问题分析与修复方案
1. 标记堆叠的核心原因
- 循环内重复定义
HTMLMarker类,且draw方法直接修改父容器panes.overlayImage的位置,导致所有标记被定位到同一坐标 div变量未声明为局部变量,全局覆盖导致后续标记替换之前的元素
2. 修复后的完整代码
JavaScript调整
function init() { var locations = [ ['500k', -33.890542, 151.274856, 1], ['1.3M', -33.923036, 151.259052, 2], ['2.3M', -34.028249, 151.157507, 3], ['750k', -33.800101, 151.287478, 4], ['200K', -33.950198, 151.259302, 5], ]; var map = new google.maps.Map(document.getElementById('mapdiv'), { zoom: 10, center: new google.maps.LatLng(-33.92, 151.25), mapTypeId: google.maps.MapTypeId.ROADMAP, }); // 将标记类移到循环外,避免重复定义 function HTMLMarker(lat, lng, price) { this.lat = lat; this.lng = lng; this.price = price; this.pos = new google.maps.LatLng(lat, lng); this.div = null; } HTMLMarker.prototype = new google.maps.OverlayView(); HTMLMarker.prototype.onRemove = function() { if (this.div && this.div.parentNode) { this.div.parentNode.removeChild(this.div); this.div = null; } } HTMLMarker.prototype.onAdd = function() { let div = document.createElement('div'); div.className = "arrow_box d-flex has-arrow"; div.innerHTML = `<div class='m-auto'>SR ${this.price}</div>`; div.style.position = 'absolute'; var panes = this.getPanes(); panes.overlayImage.appendChild(div); this.div = div; } HTMLMarker.prototype.draw = function() { var overlayProjection = this.getProjection(); var position = overlayProjection.fromLatLngToDivPixel(this.pos); if (this.div) { // 偏移坐标让标记箭头对准地图点 this.div.style.left = (position.x - 30) + 'px'; this.div.style.top = (position.y - 35) + 'px'; } } // 批量创建标记 locations.forEach(location => { var htmlmarker = new HTMLMarker(location[1], location[2], location[0]); htmlmarker.setMap(map); }); // 监听缩放事件实现动态样式切换 map.addListener('zoom_changed', function() { var zoomLevel = map.getZoom(); document.querySelectorAll('.arrow_box').forEach(marker => { if (zoomLevel >= 12) { marker.style.width = '60px'; marker.style.height = '20px'; marker.style.borderRadius = '10px'; marker.querySelector('div').style.display = 'block'; marker.classList.add('has-arrow'); } else { marker.style.width = '12px'; marker.style.height = '12px'; marker.style.borderRadius = '50%'; marker.querySelector('div').style.display = 'none'; marker.classList.remove('has-arrow'); } }); }); }
CSS调整
html,body { height: 100%; margin: 0; padding: 0; } #mapdiv { height: 100%; width: 100%; } div.arrow_box { font-size: 10px; width: 60px; height: 20px; background: blue; border-radius: 10px; color: white; cursor: pointer; overflow: hidden; transition: all 0.3s ease; } div.arrow_box img { min-width: 150px; width: auto; max-height: 100px; height: auto; border-radius: 5px; } .arrow_box.has-arrow:after { content: " "; position: absolute; top: 100%; left: 50%; transform: translateX(-50%); border: solid transparent; border-top-color: blue; border-width: 5px; pointer-events: none; }
关键修复点
- 避免全局污染:用局部变量存储标记元素,将标记类定义移到循环外
- 精准定位:修改单个标记的位置,通过偏移量让箭头底部对准地图坐标点
- 缩放动画:监听地图缩放事件,配合CSS过渡实现标记样式的平滑切换
内容的提问来源于stack exchange,提问作者Tariq
相关产品推荐
相关产品推荐

