You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 08:25:16