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

Google Maps自定义弹窗平移/缩放地图后自动回到原位置问题咨询

问题原因及解决方案

你的问题核心是Vue响应式机制和Google Maps OverlayView的运行逻辑冲突,加上DOM复用、类重复定义的问题,按以下步骤修改即可修复:

1. 先补全必须的CSS样式

自定义弹窗的容器类必须设置绝对定位,否则坐标计算会不生效:

.popup-container {
  position: absolute;
  /* 让弹窗底部中心对准经纬度坐标点 */
  transform: translate(-50%, -100%);
}
.popup-bubble-anchor {
  position: absolute;
  width: 100%;
  bottom: 0;
  left: 0;
}
/* 补充你自己的弹窗气泡样式,比如背景、圆角、内边距等 */
.popup-bubble {
  background: #fff;
  border-radius: 8px;
  padding: 12px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}

2. 调整Popup类的定义逻辑

不要每次点击marker都重复定义Popup类,将类定义提到组件初始化阶段,仅声明一次:

// 组件挂载前就定义好Popup类,不要放在loadCustomPopup里每次调用重复生成
class Popup extends window.google.maps.OverlayView {
  position;
  containerDiv;

  constructor (position, content) {
    super();
    this.position = position;

    content.classList.add('popup-bubble');

    const bubbleAnchor = document.createElement('div');
    bubbleAnchor.classList.add('popup-bubble-anchor');
    bubbleAnchor.appendChild(content);

    this.containerDiv = document.createElement('div');
    this.containerDiv.classList.add('popup-container');
    this.containerDiv.appendChild(bubbleAnchor);

    Popup.preventMapHitsAndGesturesFrom(this.containerDiv);
  }

  onAdd () {
    this.getPanes().floatPane.appendChild(this.containerDiv);
  }

  onRemove () {
    if (this.containerDiv.parentElement) {
      this.containerDiv.parentElement.removeChild(this.containerDiv);
    }
  }

  draw () {
    const divPosition = this.getProjection().fromLatLngToDivPixel(
      this.position
    );

    const display =
      Math.abs(divPosition.x) < 4000 && Math.abs(divPosition.y) < 4000
        ? 'block'
        : 'none';

    if (display === 'block') {
      this.containerDiv.style.left = divPosition.x + 'px';
      this.containerDiv.style.top = divPosition.y + 'px';
    }

    if (this.containerDiv.style.display !== display) {
      this.containerDiv.style.display = display;
    }
  }
}

3. 修改弹窗调用逻辑

解决DOM复用、多弹窗冲突、响应式劫持的问题:

  • 每次打开新弹窗先销毁旧弹窗,避免多实例冲突
  • 克隆内容DOM节点,不要复用同一个ref对应的DOM
  • 不要将popup实例、LatLng对象存为Vue响应式数据(Vue3可以用markRaw标记非响应式,Vue2不要把这些对象放在data里声明)
  • 直接用marker自带的position属性,不需要重新生成LatLng实例
loadMarkerInfoWindow (marker) {
  window.google.maps.event.addListener(marker, 'click', async () => {
    // 先销毁之前打开的弹窗
    if (this.activePopup) {
      this.activePopup.setMap(null)
      this.activePopup = null
    }

    const targetLatLng = marker.position
    this.map.panTo(targetLatLng)

    // 克隆内容DOM,避免复用同一个节点导致冲突
    const popupContent = this.$refs.googleMapsCustomPopup.cloneNode(true)
    const popup = new Popup(targetLatLng, popupContent);
    popup.setMap(this.map);
    
    // 修改injectItemCardToInfoWindow入参,往克隆的DOM里注入内容
    this.injectItemCardToInfoWindow(popupContent, marker.item);
    
    // 存储弹窗实例,不要放在data的响应式属性里
    this.activePopup = popup
  });
}

内容的提问来源于stack exchange,提问作者joe92

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 22:54:05