Appearance
PopupManager 弹窗管理器
PopupManager用于在 Cesium 场景中管理基于屏幕坐标定位的弹窗。支持多种内容类型、锚点位置、视口边界检测和自动隐藏。
构造函数
ts
constructor(viewer: Cesium.Viewer)| 参数 | 类型 | 描述 |
|---|---|---|
viewer | Cesium.Viewer | Cesium Viewer 实例 |
创建后即启用,无需额外调用初始化方法。
API
add(options: PopupOptions): PopupInstance
添加一个弹窗。返回 PopupInstance 实例,可用于后续操作。
PopupOptions
| 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
id | string | — | 弹窗唯一标识(必填) |
position | object | — | 定位坐标,见下表 |
content | string | HTMLElement | (() => string) | VueComponent | — | 弹窗内容(必填) |
anchor | AnchorPosition | 'bottom' | 锚点位置 |
offset | [number, number] | [0, 0] | 屏幕像素偏移 [x, y] |
className | string | — | 自定义 CSS 类名 |
position 坐标格式:
| 字段 | 类型 | 描述 |
|---|---|---|
lon | number | 经度(度),与 lat 成对使用 |
lat | number | 纬度(度),与 lon 成对使用 |
alt | number | 高度(米),可选,默认 0 |
cartesian | Cesium.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);注意事项
- ID 唯一性:同一 PopupManager 实例中
id必须唯一,重复添加会覆盖旧弹窗。 - 性能:大量弹窗(数百个)可能影响帧率。建议对不可见区域的弹窗及时
remove。 - Vue 组件注入:传入 Vue 组件时需确保组件已
defineComponent或为 SFC 导出对象。 destroy()后 不可再调用任何方法,需要重新创建实例。closeAll()与clear()的区别:closeAll保留实例、仅隐藏 DOM;clear销毁所有实例并移除 DOM。