4. 标记与覆盖物:添加/删除标记点、自定义标记图标、气泡弹窗(callout)、覆盖物(圆形/多边形/折线)

好,咱们今天聊点实在的。地图上光有底图肯定不够,你得往上面「画东西」。标记点、气泡、各种形状的覆盖物,这些才是地图应用的灵魂。我做了这么多年小程序地图开发,可以说,覆盖物玩得转不转,直接决定了你的产品体验好不好。

说白了,标记就是地图上的大头针,覆盖物就是各种图形。但你别小看它们,里面的坑可不少。来,我带你一个个过。

4.1 标记点(Marker)的添加与删除

添加标记点,最基础的操作。微信小程序里用 markers 属性,直接往 <map> 组件上一绑就行。

// 添加单个标记点
Page({
  data: {
    markers: [{
      id: 1,
      latitude: 39.915,
      longitude: 116.404,
      title: '天安门'
    }]
  }
})

嗯,这里要注意。每个标记点必须有一个唯一的 id。为什么?因为你要删除它的时候,得靠这个 id 来定位。我曾经在项目里吃过这个亏,忘了设 id,结果删除逻辑怎么都跑不对,排查了半天。

删除标记点就更简单了,从数组里移除对应的项就行:

// 删除 id 为 1 的标记点
removeMarker() {
  let markers = this.data.markers
  markers = markers.filter(item => item.id !== 1)
  this.setData({ markers })
}

我个人习惯把标记点的增删改查封装成一个独立的方法,方便复用。你想想看,如果每个页面都写一遍过滤逻辑,后期维护起来得多痛苦。

小技巧: 批量添加标记点时,建议用 for 循环生成 id,别用手动编号。我一般用时间戳加随机数,保证不重复。

4.2 自定义标记图标

默认的大头针太丑了,对吧?微信小程序支持自定义图标,用 iconPath 属性指定图片路径就行。

markers: [{
  id: 1,
  latitude: 39.915,
  longitude: 116.404,
  iconPath: '/images/custom_marker.png',
  width: 30,
  height: 30
}]

这里有几个坑,我得跟你说道说道。

  • 图片尺寸: 建议用 30-50px 的图标,太大了会遮挡地图内容。
  • 图片格式: 支持 png、jpg,但推荐用 png,带透明背景效果更好。
  • 路径问题: 必须用项目内的本地路径,不支持网络图片。我刚开始做的时候,直接塞了个 CDN 链接,结果图标死活不显示,查文档才发现这个限制。

如果你需要动态改变图标颜色,比如根据状态显示红绿点,我建议准备多套图标文件,用条件判断切换 iconPath。别想着用 CSS 去改,地图组件不支持那玩意儿。

重要: 自定义图标的锚点默认在图片中心。如果你想让图标底部对准坐标点,需要设置 anchor 属性:anchor: {x: 0.5, y: 1}。这个细节很容易被忽略,但效果差别很大。

4.3 气泡弹窗(Callout)

点击标记点弹出信息,这就是 callout。微信小程序里叫「气泡」,用 callout 属性配置。

markers: [{
  id: 1,
  latitude: 39.915,
  longitude: 116.404,
  title: '天安门',
  callout: {
    content: '这里是天安门广场\n开放时间:全天',
    padding: 10,
    borderRadius: 5,
    display: 'ALWAYS' // 始终显示
  }
}]

display 属性有三个值:ALWAYS(始终显示)、BYCLICK(点击显示)、BYTOUCH(触摸显示)。我个人建议用 BYCLICK,用户体验最好。始终显示的话,地图上会密密麻麻全是文字,看着就头疼。

气泡的内容支持换行,用 \n 就行。但要注意,content 是纯文本,不支持富文本。如果你想展示带样式的信息,比如加粗、颜色,那就得用自定义气泡了。

自定义气泡用 customCallout 属性,配合 slot 插槽实现。不过说实话,这个功能在微信小程序里支持得不太好,我建议能用默认气泡就用默认的,省得折腾。

避坑指南: 我曾经在项目中遇到气泡点击事件不触发的问题。后来发现,callout 的点击事件是 bindcallouttap,不是 bindmarkertap。这两个事件容易搞混,你一定要注意。

4.4 覆盖物:圆形、多边形、折线

覆盖物就是在地图上画图形。微信小程序提供了三种:圆形、多边形、折线。它们各自有对应的属性。

4.4.1 圆形覆盖物

circles 属性,适合表示范围,比如周边搜索的结果。

circles: [{
  latitude: 39.915,
  longitude: 116.404,
  radius: 1000, // 半径,单位米
  color: '#FF0000AA', // 边框颜色,支持透明度
  fillColor: '#FF000033', // 填充颜色
  strokeWidth: 2
}]

颜色值支持 8 位十六进制,后两位是透明度。比如 #FF000033 表示红色,透明度 20%。这个设计挺巧妙的,不用额外传 opacity 参数。

4.4.2 多边形覆盖物

polygons 属性,适合圈定不规则区域,比如小区范围、商圈边界。

polygons: [{
  points: [
    {latitude: 39.915, longitude: 116.404},
    {latitude: 39.925, longitude: 116.414},
    {latitude: 39.935, longitude: 116.404}
  ],
  fillColor: '#00FF0033',
  strokeColor: '#00FF00',
  strokeWidth: 2
}]

注意,points 数组至少需要 3 个点,不然构不成多边形。而且最后一个点不需要和第一个点重复,组件会自动闭合。

4.4.3 折线覆盖物

polyline 属性,适合画路线、轨迹。

polyline: [{
  points: [
    {latitude: 39.915, longitude: 116.404},
    {latitude: 39.920, longitude: 116.410},
    {latitude: 39.925, longitude: 116.415}
  ],
  color: '#0000FF',
  width: 4,
  dottedLine: false // 是否虚线
}]

折线有个很实用的属性 arrowLine,可以显示方向箭头。我在地图导航项目里就用它来指示行进方向,效果很直观。

性能建议: 覆盖物的点数量不要太多。圆形还好,多边形和折线的点如果超过 100 个,地图渲染就会明显卡顿。我建议在数据量大的时候做抽稀处理,比如只保留关键路径点。

4.5 综合示例:一个完整的地图标注场景

来,咱们写一个完整的例子。假设你要做一个「附近停车场」的功能,展示停车场位置、价格信息,并且用圆形表示服务范围。

Page({
  data: {
    markers: [{
      id: 1,
      latitude: 39.915,
      longitude: 116.404,
      iconPath: '/images/parking.png',
      width: 40,
      height: 40,
      callout: {
        content: 'A停车场\n价格:5元/小时\n空位:12个',
        padding: 8,
        borderRadius: 8,
        display: 'BYCLICK'
      }
    }, {
      id: 2,
      latitude: 39.920,
      longitude: 116.410,
      iconPath: '/images/parking.png',
      width: 40,
      height: 40,
      callout: {
        content: 'B停车场\n价格:8元/小时\n空位:3个',
        padding: 8,
        borderRadius: 8,
        display: 'BYCLICK'
      }
    }],
    circles: [{
      latitude: 39.915,
      longitude: 116.404,
      radius: 500,
      color: '#00FF00AA',
      fillColor: '#00FF0022',
      strokeWidth: 2
    }, {
      latitude: 39.920,
      longitude: 116.410,
      radius: 500,
      color: '#FF0000AA',
      fillColor: '#FF000022',
      strokeWidth: 2
    }]
  }
})

你看,这样组合起来,用户一眼就能看到附近有哪些停车场,点击标记还能看到详细信息。圆形覆盖物则直观地展示了每个停车场的服务范围。

嗯,关于标记和覆盖物,核心内容就这些。说白了就是三件事:放标记、画图形、配气泡。但每个细节都值得你花时间去打磨。我做了这么多项目,最大的体会就是:地图上的每一个像素,都代表着用户体验。别偷懒,多调调样式,多测测边界情况,你的产品会感谢你的。