Skip to content

PopupManager 弹窗管理器

PopupManager 用于在 Cesium 场景中管理基于屏幕坐标定位的弹窗。支持多种内容类型、锚点位置、视口边界检测和自动隐藏。


构造函数

ts
constructor(viewer: Cesium.Viewer)
参数类型描述
viewerCesium.ViewerCesium Viewer 实例

创建后即启用,无需额外调用初始化方法。


API

add(options: PopupOptions): PopupInstance

添加一个弹窗。返回 PopupInstance 实例,可用于后续操作。

PopupOptions

属性类型默认值描述
idstring弹窗唯一标识(必填
positionobject定位坐标,见下表
contentstring | HTMLElement | (() => string) | VueComponent弹窗内容(必填
anchorAnchorPosition'bottom'锚点位置
offset[number, number][0, 0]屏幕像素偏移 [x, y]
classNamestring自定义 CSS 类名

position 坐标格式:

字段类型描述
lonnumber经度(度),与 lat 成对使用
latnumber纬度(度),与 lon 成对使用
altnumber高度(米),可选,默认 0
cartesianCesium.Cartesian3直接传入地心坐标,与 lon/lat/alt 二选一

AnchorPosition

描述
'top'上方居中
'top-left'左上角
'top-right'右上角
'bottom'下方居中(默认)
'bottom-left'左下角
'bottom-right'右下角
'left'左侧居中
'right'右侧居中
'center'正中央

PopupInstance

属性/方法描述
id弹窗唯一标识
options创建时传入的配置项(引用)
destroy()销毁该弹窗实例

remove(id: string): boolean

移除指定 ID 的弹窗。返回 true 表示成功移除,false 表示未找到。

get(id: string): PopupInstance | undefined

获取指定 ID 的弹窗实例。

clear(): void

移除所有弹窗。

closeAll(): void

隐藏所有弹窗(保留实例,可重新通过 add 显示)。

destroy(): void

销毁 PopupManager,清除所有弹窗实例和内部事件监听。


核心机制

屏幕坐标定位

PopupManager 在 Cesium 的 postRender 事件中持续更新弹窗的屏幕坐标位置。当地球场景发生平移、缩放、旋转时,弹窗会自动跟随场景内容移动。

视口边界检测

当弹窗对应的地理坐标移出屏幕可视范围时,PopupManager 会自动隐藏该弹窗;当坐标重新进入可见区域后自动显示。这一行为无需额外配置。


代码示例

基础用法 — 字符串内容

ts
import { PopupManager } from '@air-stack/gis-cesium';

const popups = new PopupManager(viewer);

popups.add({
  id: 'city-label',
  position: { lon: 116.397, lat: 39.908, alt: 0 },
  content: '<div class="popup-title">北京</div><div class="popup-desc">首都</div>',
  anchor: 'bottom',
});

HTML 元素内容

ts
const el = document.createElement('div');
el.className = 'custom-popup';
el.innerHTML = '<strong>成都</strong><br/>经度:104.07°';

popups.add({
  id: 'chengdu',
  position: { lon: 104.07, lat: 30.57 },
  content: el,
  anchor: 'top',
  offset: [0, -8],
  className: 'city-popup',
});

回调函数内容

ts
let clickCount = 0;

popups.add({
  id: 'click-counter',
  position: { lon: -122.419, lat: 37.775 },
  content: () => `<div>点击次数:${++clickCount}</div>`,
  anchor: 'bottom-left',
});

Vue 组件内容

ts
import CustomPopup from '@/components/CustomPopup.vue';

popups.add({
  id: 'vue-popup',
  position: { lon: 13.405, lat: 52.52 },
  content: CustomPopup, // 传入 Vue 组件定义
  anchor: 'center',
});

使用地心坐标定位

ts
const positions = await Cesium.sampleTerrainMostDetailed(
  viewer.terrainProvider,
  [Cesium.Cartographic.fromDegrees(120.155, 30.274)]
);

popups.add({
  id: 'terrain-point',
  position: {
    cartesian: Cesium.Cartesian3.fromRadians(
      positions[0].longitude,
      positions[0].latitude,
      positions[0].height
    ),
  },
  content: '地形高度点',
});

批量添加与管理

ts
const cities = [
  { id: 'sh', lon: 121.47, lat: 31.23, name: '上海' },
  { id: 'gz', lon: 113.26, lat: 23.13, name: '广州' },
  { id: 'sz', lon: 114.06, lat: 22.54, name: '深圳' },
];

cities.forEach((city) => {
  popups.add({
    id: city.id,
    position: { lon: city.lon, lat: city.lat },
    content: `<b>${city.name}</b>`,
    anchor: 'bottom',
  });
});

// 移除单个
popups.remove('gz');

// 获取实例并修改
const instance = popups.get('sh');
if (instance) {
  instance.options.offset = [0, -20];
}

// 清空全部
// popups.clear();

事件联动 — 点击标记显示弹窗

ts
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);

handler.setInputAction((click) => {
  const pick = viewer.scene.pick(click.position);
  if (pick && pick.id && pick.id.properties) {
    const props = pick.id.properties;
    popups.add({
      id: 'clicked-point',
      position: {
        cartesian: pick.id.position.getValue(Cesium.JulianDate.now()),
      },
      content: `
        <div>名称:${props.name.getValue()}</div>
        <div>类型:${props.type.getValue()}</div>
      `,
      anchor: 'top',
      offset: [0, 12],
    });
  }
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

注意事项

  1. ID 唯一性:同一 PopupManager 实例中 id 必须唯一,重复添加会覆盖旧弹窗。
  2. 性能:大量弹窗(数百个)可能影响帧率。建议对不可见区域的弹窗及时 remove
  3. Vue 组件注入:传入 Vue 组件时需确保组件已 defineComponent 或为 SFC 导出对象。
  4. destroy() 不可再调用任何方法,需要重新创建实例。
  5. closeAll()clear() 的区别closeAll 保留实例、仅隐藏 DOM;clear 销毁所有实例并移除 DOM。