弹框
弹框(Popup)用于在地图上显示信息,通常与点击或悬停事件配合使用。SDK 提供了灵活的弹框功能,支持 HTML 内容、自定义样式和多种定位方式。
基础用法
创建弹框
javascript
// 创建弹框实例
const popup = new navMap.Popup({
closeButton: true, // 显示关闭按钮
closeOnClick: true, // 点击地图关闭
anchor: "bottom", // 锚点位置
offset: [0, -10], // 偏移量
className: "custom-popup", // 自定义 CSS 类
});
// 设置位置和内容
popup.setLngLat([116.3974, 39.9093])
.setHTML("<h3>北京</h3><p>中国首都</p>")
.addTo(sdk.map);设置 HTML 内容
javascript
// 设置简单 HTML
popup.setHTML("<h3>标题</h3><p>内容</p>");
// 设置复杂 HTML
popup.setHTML(`
<div class="popup-content">
<h3>${feature.properties.name}</h3>
<p>坐标: ${coordinates.join(", ")}</p>
<button onclick="doSomething()">操作</button>
</div>
`);设置文本内容
javascript
// 设置纯文本
popup.setText("这是文本内容");移除弹框
javascript
// 移除弹框
popup.remove();
// 或者
popup.removeFrom(sdk.map);点击显示弹框
基础点击弹框
javascript
sdk.on("click", (e) => {
const popup = new navMap.Popup()
.setLngLat(e.lngLat)
.setHTML("<h3>点击位置</h3>")
.addTo(sdk.map);
});点击要素显示弹框
javascript
sdk.on("click", "airport", (e) => {
const feature = e.features[0];
const popup = new navMap.Popup()
.setLngLat(e.lngLat)
.setHTML(`<h3>${feature.properties.name}</h3>`)
.addTo(sdk.map);
});使用 ClickManager
javascript
// 使用 ClickManager 简化点击弹框
sdk.clickObj.clickPopup(
"airport", // 图层ID
(feature) => { // HTML 生成函数
return `<h3>${feature.properties.name}</h3>
<p>类型: ${feature.properties.type}</p>`;
},
{ // 弹框选项
anchor: "bottom",
closeButton: true,
}
);异步内容加载
javascript
// 支持异步加载内容
sdk.clickObj.clickPopup(
"airport",
async (feature) => {
// 异步获取数据
const data = await fetch(`/api/airport/${feature.properties.id}`)
.then(r => r.json());
return `<h3>${data.name}</h3>
<p>详细信息: ${data.description}</p>`;
},
{
anchor: "bottom",
}
);悬停显示弹框
基础悬停弹框
javascript
sdk.on("mouseenter", "airport", (e) => {
const popup = new navMap.Popup({
closeButton: false, // 悬停弹框通常不显示关闭按钮
})
.setLngLat(e.lngLat)
.setHTML(`<h3>${e.features[0].properties.name}</h3>`)
.addTo(sdk.map);
});
sdk.on("mouseleave", "airport", () => {
popup.remove();
});使用 HoverManager
javascript
// 使用 HoverManager 简化悬停弹框
sdk.hover.hoverPopup(
"airport", // 图层ID
(feature) => { // HTML 生成函数
return `<div>${feature.properties.name}</div>`;
},
{ // 弹框选项
anchor: "top",
closeButton: false,
}
);异步内容加载
javascript
// 支持异步加载内容
sdk.hover.hoverPopup(
"airport",
async (feature) => {
const data = await fetch(`/api/airport/${feature.properties.id}`)
.then(r => r.json());
return `<div>${data.name}</div>`;
},
{
anchor: "top",
}
);弹框选项
锚点位置
javascript
const popup = new navMap.Popup({
anchor: "bottom", // 可选值:
// "center" | "top" | "bottom" | "left" | "right" |
// "top-left" | "top-right" | "bottom-left" | "bottom-right"
});偏移量
javascript
const popup = new navMap.Popup({
offset: [0, -10], // [x, y] 像素偏移
});
// 或者使用对象
const popup = new navMap.Popup({
offset: {
"bottom": [0, -10],
"top": [0, 10],
"left": [-10, 0],
"right": [10, 0],
},
});最大宽度
javascript
const popup = new navMap.Popup()
.setMaxWidth("300px"); // 设置最大宽度自定义 CSS 类
javascript
const popup = new navMap.Popup({
className: "custom-popup",
});
// CSS
.custom-popup {
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}弹框事件
监听弹框事件
javascript
const popup = new navMap.Popup();
// 打开事件
popup.on("open", () => {
console.log("弹框已打开");
});
// 关闭事件
popup.on("close", () => {
console.log("弹框已关闭");
});
popup.setLngLat([116.3974, 39.9093])
.setHTML("<h3>内容</h3>")
.addTo(sdk.map);动态内容更新
更新位置
javascript
// 更新弹框位置
popup.setLngLat([121.4737, 31.2304]);更新内容
javascript
// 更新 HTML 内容
popup.setHTML("<h3>新内容</h3>");
// 更新文本内容
popup.setText("新文本");跟踪地图移动
javascript
// 弹框跟随要素移动
sdk.on("move", () => {
if (popup.isOpen()) {
const newPosition = calculateNewPosition();
popup.setLngLat(newPosition);
}
});多个弹框管理
单例模式
javascript
// 确保同时只有一个弹框
let currentPopup = null;
sdk.on("click", "airport", (e) => {
// 关闭之前的弹框
if (currentPopup) {
currentPopup.remove();
}
// 创建新弹框
currentPopup = new navMap.Popup()
.setLngLat(e.lngLat)
.setHTML(`<h3>${e.features[0].properties.name}</h3>`)
.addTo(sdk.map);
});弹框管理器
javascript
class PopupManager {
constructor(map) {
this.map = map;
this.popups = new Map();
}
show(id, lngLat, html) {
// 关闭之前的弹框
this.hide(id);
// 创建新弹框
const popup = new navMap.Popup()
.setLngLat(lngLat)
.setHTML(html)
.addTo(this.map);
this.popups.set(id, popup);
}
hide(id) {
const popup = this.popups.get(id);
if (popup) {
popup.remove();
this.popups.delete(id);
}
}
hideAll() {
this.popups.forEach(popup => popup.remove());
this.popups.clear();
}
}
// 使用
const popupManager = new PopupManager(sdk.map);
popupManager.show("airport-1", [116.3974, 39.9093], "<h3>北京</h3>");样式定制
自定义样式
css
/* 全局弹框样式 */
.maplibregl-popup {
font-family: Arial, sans-serif;
}
.maplibregl-popup-content {
padding: 15px;
border-radius: 8px;
}
.maplibregl-popup-close-button {
font-size: 18px;
color: #333;
}
/* 自定义类样式 */
.custom-popup .maplibregl-popup-content {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}响应式设计
css
@media (max-width: 768px) {
.maplibregl-popup-content {
max-width: 90vw;
font-size: 14px;
}
}完整示例
javascript
let sdk;
let currentPopup = null;
async function initMap() {
sdk = new navMap.MapSDK({
container: "map",
center: [116.39, 39.9],
zoom: 10,
});
sdk.on("loadComplete", () => {
setupPopups();
});
}
function setupPopups() {
// 1. 点击显示弹框
sdk.on("click", "airport", (e) => {
const feature = e.features[0];
// 关闭之前的弹框
if (currentPopup) {
currentPopup.remove();
}
// 创建新弹框
currentPopup = new navMap.Popup({
closeButton: true,
closeOnClick: true,
anchor: "bottom",
className: "airport-popup",
})
.setLngLat(e.lngLat)
.setHTML(`
<div class="popup-content">
<h3>${feature.properties.name}</h3>
<p>类型: ${feature.properties.type}</p>
<p>坐标: ${e.lngLat.lng.toFixed(4)}, ${e.lngLat.lat.toFixed(4)}</p>
</div>
`)
.addTo(sdk.map);
// 监听弹框事件
currentPopup.on("close", () => {
console.log("弹框已关闭");
});
});
// 2. 使用 ClickManager
sdk.clickObj.clickPopup(
"airline",
async (feature) => {
// 异步加载数据
const data = await fetch(`/api/airline/${feature.properties.id}`)
.then(r => r.json());
return `
<h3>${data.name}</h3>
<p>${data.description}</p>
`;
},
{
anchor: "bottom",
closeButton: true,
maxWidth: "300px",
}
);
// 3. 使用 HoverManager
sdk.hover.hoverPopup(
"airport",
(feature) => {
return `<div>${feature.properties.name}</div>`;
},
{
anchor: "top",
closeButton: false,
}
);
}
initMap();注意事项
- 内存管理:及时移除不需要的弹框,避免内存泄漏
- 事件清理:页面卸载前移除弹框事件监听
- 性能优化:避免在频繁触发的事件中创建弹框
- 内容安全:使用
setHTML时注意 XSS 防护 - 响应式设计:考虑移动端的弹框显示效果
