custom-dom
本文回答:自定义 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);
九、常见坑
element没设 width/height → Marker 不可见;必须显式 CSS 尺寸。- 点击穿透 → 元素的 pointer-events 被全局 CSS 禁用;Marker 建议
pointer-events: auto。 - 组件数据变了但没更新:Vue/React 管理的 DOM,改 props 时组件会重绘;但 Marker 的 lngLat 要单独 setLngLat。
- Vue 3 响应式在 Marker 里断链:Marker 只拿 DOM 节点,不进入 Vue 的 diff。用函数式 props 往组件里推数据。