Skip to content

弹框

弹框(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();

注意事项

  1. 内存管理:及时移除不需要的弹框,避免内存泄漏
  2. 事件清理:页面卸载前移除弹框事件监听
  3. 性能优化:避免在频繁触发的事件中创建弹框
  4. 内容安全:使用 setHTML 时注意 XSS 防护
  5. 响应式设计:考虑移动端的弹框显示效果