问题初现

公司项目需要在地图上展示超过一万个覆盖物,数量最多可达到两万以上。一开始,我们发现聚合点的生成速度尚可,大约需要500毫秒。然而,当聚合点生成完毕后,用户在操作地图,比如进行缩放或移动时,会发现地图运行变得非常缓慢。在日常生活中,当用户快速缩放地图以查看更广区域时,页面常常会停滞不动,这极大地影响了使用感受。

新问题暴露

在后续的改进过程中,我们又发现了两个问题。首先,如果在聚合点加载之前用户对地图进行了操作,那么加载过程会变得极其缓慢,甚至超过1000毫秒。其次,每次删除聚合点后再重新添加,速度都会逐渐减慢。举个例子,最初加载聚合点只需要100毫秒,但随着操作次数的增加,加载速度明显变慢。这些问题严重影响了项目在实际使用中的效果。

卡顿多情况

地图上除了聚合点问题,还存在其他可能导致操作不畅的情况。首先,地图覆盖物过多时,操作起来会变得缓慢。这是因为百度地图需要实时计算这些覆盖物的位置,而覆盖物数量过多,计算量巨大,从而影响了界面的流畅度。其次,在同时添加众多覆盖物时,也会出现卡顿现象。在处理复杂地图场景时,这两种卡顿现象尤为常见。

方案一思路

/**
 * @param point {BMap.Point}
 */
function isVisible(point){
  const bounds = bMapInstance.getBounds()
  return bounds.containsPoint(point)
}

为解决地图操作卡顿,我们实施了一种监听百度地图相关事件的策略。当发生moving或zoomstart事件时,地图上的所有覆盖物会被移除;而在moveend或zoomend事件触发后,用户能看到的地图部分将被重新加载。线上测试表明,使用movestart时出现问题,用户点击地图激活movestart后,并未触发moveend,功能表现异常。这可能是由于浏览器版本较旧,存在兼容性问题。

/**
 * @param bounds {BMap.Bounds}
 * @return {boolean}
 */
function isVisible(bounds) {
  const bMapBounds = bMapInstance.getBounds()
  return !!bMapBounds.intersects(bounds)
}

方案二要点

操作地图时,推荐使用moving事件。movestart事件在用户点击地图时激活,但后续不会触发moveend事件,这会导致功能无法正常工作。此外,还需实施防抖措施,因为moveend和zoomend事件的发生并不代表操作已结束,短时间内还有可能再次触发moving和zoomstart事件。所以必须要有防抖操作,避免功能异常。

分批次添加

处理因覆盖物过多导致的运行不畅,我们采取分阶段向地图中添加覆盖物。确保每阶段添加不会造成明显延迟,并在每阶段之间设置定时器,以便页面有足够时间处理用户操作和渲染。虽然这样做会延长操作时间,但能显著改善卡顿问题。这种方法在大型地图项目中使用,大大提升了用户的操作感受。

export function addStartAndEndEventToBMap(bMapInstance, start, end) {
  let doing = false
  function _start() {
    if (doing) return
    doing = true
    start()
  }
  function _end() {
    if (!doing) return
    doing = false
    setTimeout(() => !doing && end(), 200)
  }
  bMapInstance.addEventListener("zoomstart", () => {
    _start()
  })
  bMapInstance.addEventListener("moving", () => {
    _start()
  })
  bMapInstance.addEventListener("moveend", () => {
    _end()
  })
  bMapInstance.addEventListener("zoomend", () => {
    _end()
  })
}

这是对地图项目覆盖层出现卡顿现象及其解决方法的详尽阐述。遇到这些棘手的卡顿,我们持续寻求有效的解决途径。在使用地图软件时,你是否也遭遇过类似的卡顿?不妨点赞并转发这篇文章,同时在评论区留下你的看法。