返回

Google地图自定义标记位置错乱问题详解与解决

vue.js

Google 地图自定义标记位置错误问题解析与解决

在使用 Google Maps JavaScript API 构建地图应用时,自定义标记的位置错乱是一个常见的问题。通常表现为标记没有出现在其对应的地理坐标上,而是集中在屏幕的一条线上,甚至在拖动地图后位置依然错乱。本文将深入探讨此问题的成因并提供一系列解决方案。

问题剖析

问题的核心在于如何将 HTML 元素准确地放置在 Google 地图的地理位置上。如果只是使用默认的 Google Maps API 的标记功能,问题并不容易出现,但当你尝试用自定义 HTML 元素来标记地图时,你需要做额外的工作来保持其位置的准确。

通常有两种主要的方式处理自定义标记:使用 google.maps.Markericon 选项自定义图片,或使用 google.maps.OverlayView 来完全控制自定义标记的渲染。本案例使用了 google.maps.OverlayView 的方式。

如果代码没有正确处理 OverlayViewdraw() 方法中的位置计算, 很容易出现错位现象。当 draw() 函数在不同情况下被调用,你需要重新计算 marker 位置;特别是地图在移动或者缩放后,这个计算至关重要。如果没有准确将地理坐标转换为屏幕上的像素位置,自定义标记便会出现在错误的位置,也就是问题中所见的垂直线排布的情况。

以下是具体可能导致问题的部分原因:

  • 位置更新不及时: OverlayViewdraw() 函数在每次地图视口改变或地图重绘时会被调用。假如你在此方法内,没有正确转换地理位置坐标到地图上的像素位置,或者没有考虑到偏移量,标记位置会出错。
  • draw() 中的错误计算:draw()函数中,经常错误的使用 div 元素的 lefttop 属性,因为如果想做到中心点定位标记,我们需要计算它的偏移值,而没有考虑到这点容易导致计算错误。
  • 事件侦听器错误配置: bounds_changed 事件的侦听器用来判断地图是否被用户操作,但它只是一个触发器,真正的地图元素的放置,需要在每一次 OverlayViewdraw 调用时,重新计算位置,它需要正确转换地理坐标到像素坐标。
  • 异步操作不正确: 如果自定义标记的 HTML 内容需要异步加载 (如图像),可能会在初始渲染时没有正确的尺寸,从而影响其位置的准确计算。

解决方案

下面列出了一系列针对性解决方案,并附有代码示例及步骤。

1. 精准计算像素位置

核心在于 OverlayViewdraw() 方法,必须正确将经纬度坐标转换为像素坐标,然后设置标记的 lefttop 值。特别是针对带有偏移量的自定义标记,必须要考虑到这个偏移量。

overlay.draw = function() {
  const projection = this.getProjection();
  const positionPixel = projection.fromLatLngToDivPixel(position);

  if (positionPixel) {
    // Center the marker based on its width
    markerContent.style.left = `${positionPixel.x - (95 / 2)}px`;
    markerContent.style.top = `${positionPixel.y - (95 / 2)}px`;
  }
};

步骤:

  1. OverlayViewdraw() 方法中,使用 this.getProjection() 获取当前地图的投影。
  2. 使用投影的 fromLatLngToDivPixel(position) 方法,将 LatLng 坐标转换为相对于地图容器左上角的像素坐标。
  3. 使用计算出的 xy 值正确设置 markerContent 元素 lefttop 样式属性。务必考虑到偏移,确保自定义元素的中点与实际地图的经纬度重合。

2. 异步处理和位置刷新

确保当自定义标记的内容,例如图片是异步加载时,draw() 方法在内容加载完成后会被重新触发,重新计算正确位置。

步骤:

  1. 在图像加载完成后,触发 overlaydraw 方法

    const img = markerContent.querySelector('img');
    img.onload = () => {
        overlay.draw(); //  图像加载完毕,刷新marker的位置。
    };
    

注意: 图像加载的失败的情况也应该考虑到,例如使用 img.onerror 去捕获错误,并且给出合适的降级策略。

3. 保证 overlay 初始化完成才加入标记

确保 Google 地图初始化加载完毕以及 OverlayView 创建完毕后,才在 draw() 中处理位置。这通常可以在地图加载完成后通过 tilesloaded 事件来检测。

   google.maps.event.addListener(this.map, 'tilesloaded', () => {
          this.googleMapsLoaded = true;
          console.log('Google Maps loaded successfully');
  
          // Add markers once map is ready and fit the map bounds
          this.places.forEach(place => {
            this.createCustomMarker(place);
          });
          if (!this.mapInteracted) {
            this.fitBounds();
          }
        });

步骤:

  1. 监听 google map tilesloaded 事件。
  2. 只有当地图资源加载完毕之后,才开始调用 createCustomMarker 创建标记,确保自定义标记被添加到已完全初始化的地图上。

4. 清理和优化事件侦听器

确保旧的侦听器和标记被移除,特别是当有动态的数据更新或者使用 watch 函数来观察地图数据的变化。

 clearMarkers() {
  this.markers.forEach(marker => {
      marker.setMap(null);
      });
    this.markers = [];
    }
 }

步骤:

  1. 创建一个 clearMarkers() 方法用于清除所有的 OverlayView 对象。
  2. 确保在使用新的位置数组生成新的 marker 之前,调用 clearMarkers ,避免新旧位置的干扰。

安全建议

  • 限制事件监听: 不要在过多的事件监听中绑定太多逻辑,避免浏览器卡顿。

  • 代码模块化: 将创建标记和处理逻辑分割成独立的函数,增强代码可读性和可维护性。

  • 错误处理: 对地图初始化失败或位置信息获取错误的情况进行合理的错误处理。

  • 异步错误处理: 对于异步加载的内容,特别是图片加载失败,提供对应的提示。

总结

解决 Google 地图自定义标记位置错乱问题的关键,在于精确计算自定义标记的屏幕位置,尤其当涉及地图移动或者缩放时,每次 OverlayViewdraw() 方法被调用时都要重新计算。通过精准的位置计算,以及合理的事件监听处理,确保了在不同情况下,自定义标记都可以准确的放置在地图之上。使用本教程中提到的步骤,并加以合理优化,可以有效提升地图应用的可靠性和用户体验。