唯杰地图 logo唯杰地图
VJMAP
EN

custom-dom

vjmap/references/marker/custom-dom.md 4.6 KB181 行
原文

本文回答:自定义 DOM 作为 Marker —— 纯图标 / 图+文 / Vue / React / 复杂卡片。

一、纯图标(CSS 背景图)

<style>
  .m-pin {
    width: 32px; height: 32px;
    background: url("/icons/pin.png") center/cover no-repeat;
    cursor: pointer;
  }
</style>
const el = document.createElement("div");
el.className = "m-pin";
new vjmap.Marker({ element: el, anchor: "bottom" })
  .setLngLat([lng, lat]).addTo(map);

二、图 + 文(父 div 包两个子 div)

<style>
  .m-wrap { text-align: center; cursor: pointer; }
  .m-icon {
    width: 32px; height: 32px; margin: 0 auto;
    background: url("/icons/pin.png") center/cover no-repeat;
  }
  .m-text {
    font-size: 12px; color: #fff; background: rgba(0, 0, 0, 0.6);
    padding: 2px 6px; border-radius: 3px; margin-top: 2px;
  }
</style>
const wrap = document.createElement("div");
wrap.className = "m-wrap";

const icon = document.createElement("div");
icon.className = "m-icon";

const text = document.createElement("div");
text.className = "m-text";
text.textContent = "站点 A";

wrap.appendChild(icon);
wrap.appendChild(text);

new vjmap.Marker({ element: wrap, anchor: "bottom" })
  .setLngLat([lng, lat]).addTo(map);

三、复杂卡片

const el = document.createElement("div");
el.innerHTML = `
  <div style="background:#fff;border-radius:8px;box-shadow:0 2px 8px rgba(0,0,0,.15);
              padding:8px 12px;font-size:12px;min-width:120px">
    <div style="font-weight:bold">站点 A</div>
    <div style="color:#666">当前状态:正常</div>
    <div style="color:#0af">温度 26℃</div>
  </div>
`;
new vjmap.Marker({ element: el, anchor: "bottom" })
  .setLngLat([lng, lat]).addTo(map);

四、Vue 3 组件作为 Marker

<!-- MyPin.vue -->
<template>
  <div class="pin" :style="{ background: color }">
    {{ label }}
  </div>
</template>
<script setup>
defineProps(["color", "label"]);
</script>
<style scoped>
.pin { padding: 4px 8px; color: #fff; border-radius: 4px; cursor: pointer; }
</style>

使用:

import { createApp } from "vue";
import MyPin from "./MyPin.vue";

function createVuePin(props) {
  const container = document.createElement("div");
  createApp(MyPin, props).mount(container);
  return container;
}

const el = createVuePin({ color: "#f00", label: "Site A" });
const marker = new vjmap.Marker({ element: el, anchor: "bottom" })
  .setLngLat([lng, lat]);
marker.addTo(map);

注意:组件销毁时要同时 marker.remove(),否则 DOM 残留。

五、React 组件作为 Marker

// MyPin.jsx
export function MyPin({ color, label }) {
  return <div style={{ background: color, padding: 4, borderRadius: 4 }}>{label}</div>;
}
import ReactDOM from "react-dom/client";

const container = document.createElement("div");
ReactDOM.createRoot(container).render(<MyPin color="#f00" label="Site A"/>);

const marker = new vjmap.Marker({ element: container, anchor: "bottom" })
  .setLngLat([lng, lat]);
marker.addTo(map);

卸载时:

marker.remove();
// 可选:
// reactRoot.unmount();

六、动态更新 Marker 内容(不重建)

const label = document.createElement("div");
label.textContent = "0℃";
const marker = new vjmap.Marker({ element: label }).setLngLat([lng, lat]);
marker.addTo(map);

setInterval(() => {
  label.textContent = `${(Math.random() * 30).toFixed(1)}℃`;
}, 1000);

直接改 DOM 内容,不用重建 Marker。

七、拖拽时实时更新

marker.setDraggable(true);
marker.on("drag", () => {
  const ll = marker.getLngLat();
  console.log("dragging to", ll);
});
marker.on("dragend", () => {
  console.log("dropped at", marker.getLngLat());
});

八、克隆 Marker 作为 hover 指示

const clone = marker.getElement().cloneNode(true);
clone.style.transform = "scale(1.2)";
const hoverMarker = new vjmap.Marker({ element: clone })
  .setLngLat(marker.getLngLat());
hoverMarker.addTo(map);

九、常见坑

  1. element 没设 width/height → Marker 不可见;必须显式 CSS 尺寸。
  2. 点击穿透 → 元素的 pointer-events 被全局 CSS 禁用;Marker 建议 pointer-events: auto。
  3. 组件数据变了但没更新:Vue/React 管理的 DOM,改 props 时组件会重绘;但 Marker 的 lngLat 要单独 setLngLat。
  4. Vue 3 响应式在 Marker 里断链:Marker 只拿 DOM 节点,不进入 Vue 的 diff。用函数式 props 往组件里推数据。