微信小程序地图自定义标记:从 Web 到小程序的踩坑实录

一、需求

【截图位置:放你的设计稿,显示图标+文字的气泡样式】

地图上显示带时间信息的打卡点:

  • 起点:开图标 + 09:12 + 地点名称
  • 终点:关图标 + 18:30 + 地点名称

二、Web 端怎么做

高德/百度地图 JS API 支持直接传 HTML:

const marker = new AMap.Marker({
  content: `
    <div class="bubble">
      <img src="/icon/start.svg"/>
      <span>09:12 起点位置</span>
    </div>
  `,
  position: [116.397428, 39.90923],
  offset: new AMap.Pixel(-20, -30)
});

CSS 随便写,响应式、动画、交互都能做。完事。

三、小程序为什么做不了

微信小程序的 Map 组件:

const marker = {
  id: 1,
  latitude: 30.5928,
  longitude: 114.3055,
  iconPath: '/static/marker.png',  // ← 只能传图片路径!
  width: 40,
  height: 40
};

没有 content 属性,不支持 HTML,只认图片。

小程序运行环境是 JSCore,不是浏览器:

  • 没有 DOM
  • 没有 CSS 渲染引擎
  • 无法对元素截图

四、尝试过的方案

方案 1:预生成所有图片(放弃)

想法:让设计师把各种时间组合都出图。

问题:

  • 时间是动态的,无法穷举
  • 地点名称也是动态的
  • 包体积爆炸

结论:静态方案不可行。


方案 2:html2canvas(失败)

想法:Web 端常用 html2canvas 把 DOM 转图片,小程序应该也行。

代码:

import html2canvas from 'html2canvas';
const canvas = await html2canvas(document.getElementById('bubble'));

报错:

document is not defined

失败原因:小程序没有 document 对象。


方案 3:小程序原生组件 + 截图(失败)

想法:用 cover-view 渲染气泡,然后用小程序 API 截图。

代码:

<cover-view id="bubble" style="position:fixed;left:-9999px">
  <cover-image src="/icon.svg"/>
  <cover-view>09:12</cover-view>
</cover-view>

问题:

  • 小程序没有通用的 DOM 截图 API
  • canvasToTempFilePath 只能导出 Canvas,不能导出任意组件
  • left: -9999px 的元素获取不到尺寸

失败原因:小程序没有截图能力。


方案 4:base64 图片(失败)

想法:把 SVG 转成 base64,直接作为 iconPath。

代码:

const marker = {
  iconPath: 'data:image/svg+xml;base64,PHN2Zy...'
};

报错:

iconPath 不支持 base64 格式

失败原因:小程序 Map 只支持本地路径或网络 URL。


五、最终方案:Canvas 绘制

唯一可行的路:用 Canvas 2D API 把气泡画出来,导出临时图片。

流程:

配置(文字、图标)→ Canvas 绘制 → 导出临时文件 → 设置 marker

核心代码:

// 1. 获取 Canvas(必须在组件实例上下文中)
const query = uni.createSelectorQuery().in(instance.proxy);
const { node: canvas } = await new Promise(r => 
  query.select('#bubble-canvas').fields({ node: true }, r).exec()
);
const ctx = canvas.getContext('2d');

// 2. 计算尺寸
const textWidth = ctx.measureText('09:12 起点').width;
const width = padding + iconSize + gap + textWidth + padding;

// 3. 绘制
ctx.fillStyle = '#FFF';
roundRect(ctx, 0, 0, width, height, 30);  // 圆角背景
ctx.fill();

const icon = await loadImage('/static/icon.svg', canvas);
ctx.drawImage(icon, padding, y, iconSize, iconSize);

ctx.fillStyle = '#1A1A1A';
ctx.fillText('09:12 起点', textX, height / 2);

// 4. 导出
const { tempFilePath } = await uni.canvasToTempFilePath({ canvas });

思想是这个就行了,具体怎么用canvas绘制可以用ai写工具函数,这里有些函数篇幅太长,就没有截图

使用:

<BubbleRenderer 
  text="09:12 起点位置"
  icon-path="/static/icon/map_tags_20.svg"
  @rendered="(path, w, h) => addMarker(path, w, h)"
/>

六、踩坑记录

坑现象解决
Canvas 获取不到node 为 undefined.in(instance.proxy) 指定组件上下文
roundRect 报错API 不支持用 arcTo 手动画圆角
marker 宽度无效[object Object]Math.round(Number(width)) 转整数
SVG 加载失败图标不显示加超时处理,失败画占位圆

七、总结

Web 端:HTML + CSS → 直接渲染
小程序:Canvas API → 手动绘制 → 导出图片

小程序没有 DOM,地图只认图片,所以必须用 Canvas 把"组件"画成"图片"。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐