一、前言:为什么需要专业的弹窗组件?
在现代WebGIS应用中,地图弹窗是用户与地理信息交互的核心界面。一个优秀的弹窗组件不仅能清晰展示数据,更能提升用户体验和应用专业性。Cesium作为强大的3D地球可视化库,虽然功能丰富,但并未提供开箱即用的高级弹窗功能。本文将介绍如何实现一个功能完备、样式优雅的Cesium弹窗组件。
二、功能亮点
-
互斥显示:同时只显示一个弹窗,避免界面混乱
-
高度自定义:支持HTML字符串和函数生成动态内容
-
智能定位:自动跟随实体位置,相机移动时实时更新
-
优雅动画:平滑的显示/隐藏过渡效果
-
响应式设计:自适应不同屏幕尺寸和内容长度
核心实现代码
class CesiumPopup {
constructor(viewer, options = {}) {
this.viewer = viewer;
this.options = Object.assign({
offset: [0, -45],
closeButton: true,
maxWidth: 320,
className: 'cesium-popup'
}, options);
this.currentPopup = null;
this.handler = null;
this.init();
}
// 初始化事件处理
init() {
const self = this;
// 使用屏幕空间事件处理器
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
// 监听左键点击事件
this.handler.setInputAction(function(event) {
const picked = self.viewer.scene.pick(event.position);
if (Cesium.defined(picked) && picked.id) {
// 如果点击的是实体且有popupContent属性
if (picked.id.popupContent) {
self.showPopup(picked.id, event.position);
}
} else {
// 点击空白处关闭弹窗
self.closePopup();
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
}
// 显示弹窗
showPopup(entity, position) {
// 关闭现有弹窗
this.closePopup();
// 创建弹窗容器
const popup = document.createElement('div');
popup.className = this.options.className;
popup.style.maxWidth = this.options.maxWidth + 'px';
// 添加关闭按钮
if (this.options.closeButton) {
const closeBtn = document.createElement('button');
closeBtn.className = 'close-btn';
closeBtn.innerHTML = '×';
closeBtn.onclick = () => this.closePopup();
popup.appendChild(closeBtn);
}
// 设置弹窗内容
if (typeof entity.popupContent === 'string') {
popup.innerHTML += entity.popupContent;
} else if (typeof entity.popupContent === 'function') {
popup.innerHTML += entity.popupContent();
} else {
console.warn('Entity has no valid popupContent');
return;
}
// 添加到DOM
this.viewer.container.appendChild(popup);
this.currentPopup = {
element: popup,
entity: entity
};
// 定位弹窗
this.positionPopup(position);
// 监听相机变化事件,更新弹窗位置
this.cameraMoveHandler = this.viewer.camera.moveEnd.addEventListener(() => {
if (this.currentPopup) {
const newPosition = Cesium.SceneTransforms.wgs84ToWindowCoordinates(
this.viewer.scene,
entity.position.getValue(Cesium.JulianDate.now())
);
if (newPosition) {
this.positionPopup(newPosition);
}
}
});
}
// 其他方法...
}
三、使用指南
基本集成
// 初始化Cesium Viewer
const viewer = new Cesium.Viewer('cesiumContainer', {
terrainProvider: Cesium.createWorldTerrain()
});
// 创建弹窗组件实例
const popupManager = new CesiumPopup(viewer);
添加带弹窗的实体
// 添加简单实体
const entity = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883),
billboard: {
image: 'pin.png',
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
width: 40,
height: 40
},
popupContent: `
<h3>位置标题</h3>
<p>详细信息描述</p>
<p>更多内容...</p>
`
});
// 添加动态内容实体
const dynamicEntity = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(-74.5, 40.5),
billboard: {
image: 'pin.png',
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
width: 40,
height: 40
},
popupContent: function() {
const date = new Date();
return `
<h3>动态内容</h3>
<p>当前时间: ${date.toLocaleTimeString()}</p>
<p>随机值: ${Math.random()}</p>
`;
}
});
四、高级应用场景
1. 实时数据监控
// 创建实时监控点
function createMonitoringPoint(position, title) {
return viewer.entities.add({
position: position,
billboard: {
image: 'sensor.png',
verticalOrigin: Cesium.VerticalOrigin.BOTTOM
},
popupContent: function() {
// 模拟实时数据
const temperature = (20 + Math.random() * 10).toFixed(1);
const humidity = (50 + Math.random() * 30).toFixed(1);
return `
<h3>${title}监测站</h3>
<p><strong>温度:</strong> ${temperature}°C</p>
<p><strong>湿度:</strong> ${humidity}%</p>
<p><strong>更新时间:</strong> ${new Date().toLocaleTimeString()}</p>
<div class="alert ${temperature > 25 ? 'alert-warning' : 'alert-info'}">
${temperature > 25 ? '高温警告' : '状态正常'}
</div>
`;
}
});
}
2. 交互式操作面板
// 创建带交互操作的弹窗
function createInteractivePoint(position, title) {
return viewer.entities.add({
position: position,
billboard: {
image: 'interactive.png',
verticalOrigin: Cesium.VerticalOrigin.BOTTOM
},
popupContent: function() {
return `
<h3>${title}</h3>
<p>选择操作类型:</p>
<div class="button-group">
<button onclick="handleAction('detail')">查看详情</button>
<button onclick="handleAction('edit')">编辑信息</button>
<button onclick="handleAction('navigate')">导航到此</button>
</div>
<div id="action-result"></div>
`;
}
});
}
五、以下是全部的实现逻辑(需要本机搭建服务)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cesium弹窗组件 - 修复关闭按钮点击问题</title>
<script src="https://cesium.com/downloads/cesiumjs/releases/1.87/Build/Cesium/Cesium.js"></script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.87/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
<style>
html, body, #cesiumContainer {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
font-family: 'Microsoft YaHei', Arial, sans-serif;
}
/* 弹窗样式 */
.cesium-popup {
position: absolute;
background: white;
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
padding: 20px;
min-width: 240px;
max-width: 320px;
z-index: 1000;
transform: translate(-50%, -100%);
margin-top: -20px;
transition: opacity 0.3s ease;
display: none;
}
.cesium-popup.active {
display: block;
}
.cesium-popup:after {
content: '';
position: absolute;
top: 100%;
left: 50%;
margin-left: -12px;
border-width: 12px;
border-style: solid;
border-color: white transparent transparent transparent;
}
.cesium-popup h3 {
margin: 0 0 12px 0;
font-size: 18px;
color: #2c3e50;
border-bottom: 2px solid #3498db;
padding-bottom: 8px;
}
.cesium-popup p {
margin: 8px 0;
font-size: 14px;
color: #34495e;
line-height: 1.5;
}
.cesium-popup .close-btn {
position: absolute;
top: 12px;
right: 15px;
cursor: pointer;
font-size: 20px;
color: #95a5a6;
background: none;
border: none;
font-weight: bold;
z-index: 1001;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
pointer-events: auto !important;
}
.cesium-popup .close-btn:hover {
color: #e74c3c;
background-color: #f5f5f5;
}
.cesium-popup .popup-content {
pointer-events: none;
}
.cesium-popup .popup-content * {
pointer-events: auto;
}
.header {
position: absolute;
top: 0;
left: 0;
width: 100%;
background: rgba(42, 42, 42, 0.8);
color: white;
padding: 15px 20px;
z-index: 1000;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
}
.header h1 {
margin: 0;
font-size: 22px;
font-weight: 500;
}
.header p {
margin: 5px 0 0;
font-size: 14px;
color: #ecf0f1;
}
.control-panel {
position: absolute;
top: 100px;
right: 20px;
background: rgba(255, 255, 255, 0.9);
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
z-index: 999;
width: 250px;
}
.control-panel h3 {
margin-top: 0;
color: #2c3e50;
border-bottom: 1px solid #bdc3c7;
padding-bottom: 8px;
}
.control-panel p {
font-size: 14px;
color: #7f8c8d;
line-height: 1.4;
}
.control-panel button {
background: #3498db;
color: white;
border: none;
padding: 8px 12px;
border-radius: 4px;
cursor: pointer;
margin-top: 10px;
width: 100%;
}
.control-panel button:hover {
background: #2980b9;
}
.debug-info {
position: absolute;
top: 150px;
left: 20px;
background: rgba(255, 255, 255, 0.9);
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
z-index: 999;
width: 250px;
max-height: 300px;
overflow-y: auto;
}
.debug-info h3 {
margin-top: 0;
color: #2c3e50;
border-bottom: 1px solid #bdc3c7;
padding-bottom: 8px;
}
#eventLog {
font-size: 12px;
line-height: 1.4;
color: #34495e;
}
.status-bar {
position: absolute;
bottom: 20px;
right: 20px;
background: rgba(255, 255, 255, 0.9);
padding: 10px 15px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
z-index: 999;
font-size: 14px;
color: #2c3e50;
}
</style>
</head>
<body>
<div class="header">
<h1>Cesium弹窗组件 - 修复关闭按钮点击问题</h1>
<p>点击地图上的扎点查看弹窗效果,点击关闭按钮可以关闭弹窗</p>
</div>
<div id="cesiumContainer"></div>
<div class="control-panel">
<h3>控制面板</h3>
<p>点击地图上的任意标记点查看详细信息。</p>
<p>点击弹窗右上角的关闭按钮可以关闭弹窗。</p>
<button id="addPoint">添加随机扎点</button>
<button id="clearAll">清除所有扎点</button>
</div>
<div class="debug-info">
<h3>事件日志</h3>
<div id="eventLog">事件日志将显示在这里</div>
</div>
<div class="status-bar" id="statusBar">
就绪 - 点击地图上的扎点查看信息
</div>
<script>
// 设置Cesium Ion访问令牌
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI1ZDVlMTQ5ZC1kZTYxLTQzM2MtODU3ZS1kMjA4YzZiOTBjZGEiLCJpZCI6Mjc5NTY3LCJpYXQiOjE3NDA2NDMyODJ9.ScSAm-1y-8DT0uq4DXGt8NQrEPVo3b_f-P_5bghF7rU'
// 初始化Cesium Viewer
const viewer = new Cesium.Viewer('cesiumContainer', {
terrainProvider: Cesium.createWorldTerrain(),
baseLayerPicker: false,
homeButton: false,
sceneModePicker: false,
navigationHelpButton: false,
animation: false,
timeline: false,
fullscreenButton: false,
geocoder: false
});
// 设置初始视角
viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(-75.0, 40.0, 5000000),
orientation: {
heading: 0,
pitch: -Math.PI/2,
roll: 0
}
});
// 更新状态栏
function updateStatus(message) {
const statusBar = document.getElementById('statusBar');
statusBar.textContent = message;
}
// 更新事件日志
function logEvent(message) {
const eventLog = document.getElementById('eventLog');
const timestamp = new Date().toLocaleTimeString();
eventLog.innerHTML = `<span style="color:#777;">[${timestamp}]</span> ${message}<br>` + eventLog.innerHTML;
// 保持日志长度
if (eventLog.children.length > 20) {
eventLog.removeChild(eventLog.lastChild);
}
}
// 定义弹窗组件类
class CesiumPopup {
constructor(viewer, options = {}) {
this.viewer = viewer;
this.options = Object.assign({
offset: [0, -45],
closeButton: true,
maxWidth: 320,
className: 'cesium-popup'
}, options);
this.popups = new Map(); // 存储所有弹窗
this.activePopup = null; // 当前活动弹窗
this.handler = null;
this.preUpdateHandler = null;
this.init();
}
// 初始化事件处理
init() {
const self = this;
// 使用屏幕空间事件处理器
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
// 监听左键点击事件
this.handler.setInputAction(function(event) {
const picked = self.viewer.scene.pick(event.position);
if (Cesium.defined(picked) && picked.id) {
// 如果点击的是实体且有popupContent属性
if (picked.id.popupContent) {
self.showPopup(picked.id, event.position);
updateStatus(`已显示弹窗: ${picked.id.name || "未命名实体"}`);
logEvent(`地图点击: 显示弹窗 ${picked.id.name || "未命名实体"}`);
}
} else {
// 点击空白处关闭弹窗
self.hideAllPopups();
updateStatus("已关闭所有弹窗");
logEvent("地图空白处点击: 关闭所有弹窗");
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
// 监听场景更新事件,实时更新所有弹窗位置
this.preUpdateHandler = this.viewer.scene.preUpdate.addEventListener(function() {
self.updateAllPopupsPosition();
});
}
// 为实体创建弹窗
createPopupForEntity(entity) {
if (this.popups.has(entity.id)) return;
// 创建弹窗容器
const popup = document.createElement('div');
popup.className = this.options.className;
popup.style.maxWidth = this.options.maxWidth + 'px';
popup.id = `popup-${entity.id}`;
// 添加关闭按钮 - 使用单独的容器
if (this.options.closeButton) {
const closeBtnContainer = document.createElement('div');
closeBtnContainer.style.position = 'absolute';
closeBtnContainer.style.top = '10px';
closeBtnContainer.style.right = '10px';
closeBtnContainer.style.zIndex = '1002';
const closeBtn = document.createElement('button');
closeBtn.className = 'close-btn';
closeBtn.innerHTML = '×';
closeBtn.title = '关闭弹窗';
// 直接绑定点击事件
closeBtn.onclick = (e) => {
e.stopPropagation();
e.preventDefault();
this.hidePopup(entity.id);
updateStatus(`已关闭弹窗: ${entity.name || "未命名实体"}`);
logEvent(`关闭按钮点击: 关闭弹窗 ${entity.name || "未命名实体"}`);
return false;
};
closeBtnContainer.appendChild(closeBtn);
popup.appendChild(closeBtnContainer);
}
// 创建内容容器
const contentContainer = document.createElement('div');
contentContainer.className = 'popup-content';
// 设置弹窗内容
if (typeof entity.popupContent === 'string') {
contentContainer.innerHTML = entity.popupContent;
} else if (typeof entity.popupContent === 'function') {
contentContainer.innerHTML = entity.popupContent();
} else {
console.warn('Entity has no valid popupContent');
return;
}
popup.appendChild(contentContainer);
// 阻止弹窗点击事件冒泡到地图
popup.addEventListener('click', function(e) {
e.stopPropagation();
logEvent("弹窗点击: 事件已阻止冒泡");
});
// 添加到DOM
this.viewer.container.appendChild(popup);
// 存储弹窗引用
this.popups.set(entity.id, {
element: popup,
entity: entity
});
logEvent(`创建弹窗: ${entity.name || "未命名实体"}`);
}
// 显示指定实体的弹窗
showPopup(entity, position) {
// 隐藏所有弹窗
this.hideAllPopups();
// 如果该实体没有弹窗,先创建一个
if (!this.popups.has(entity.id)) {
this.createPopupForEntity(entity);
}
// 显示弹窗
const popup = this.popups.get(entity.id);
popup.element.classList.add('active');
this.activePopup = popup;
// 定位弹窗
this.positionPopup(popup, position);
logEvent(`显示弹窗: ${entity.name || "未命名实体"}`);
}
// 更新所有弹窗位置
updateAllPopupsPosition() {
for (const [entityId, popup] of this.popups) {
if (popup.element.classList.contains('active')) {
const position = Cesium.SceneTransforms.wgs84ToWindowCoordinates(
this.viewer.scene,
popup.entity.position.getValue(Cesium.JulianDate.now())
);
if (position) {
this.positionPopup(popup, position);
}
}
}
}
// 定位弹窗
positionPopup(popup, position) {
const popupElement = popup.element;
const offset = this.options.offset;
// 计算弹窗位置
const x = position.x + offset[0];
const y = position.y + offset[1];
// 检查弹窗是否在视图内
const containerRect = this.viewer.container.getBoundingClientRect();
const popupRect = popupElement.getBoundingClientRect();
// 如果弹窗超出右边界
if (x + popupRect.width / 2 > containerRect.width) {
popupElement.style.left = (containerRect.width - popupRect.width / 2) + 'px';
}
// 如果弹窗超出左边界
else if (x - popupRect.width / 2 < 0) {
popupElement.style.left = (popupRect.width / 2) + 'px';
}
// 正常情况
else {
popupElement.style.left = x + 'px';
}
// 如果弹窗超出上边界
if (y - popupRect.height < 0) {
popupElement.style.top = (popupRect.height + 10) + 'px';
popupElement.style.transform = 'translate(-50%, 0)';
popupElement.style.marginTop = '0';
}
// 正常情况
else {
popupElement.style.top = y + 'px';
popupElement.style.transform = 'translate(-50%, -100%)';
popupElement.style.marginTop = '-20px';
}
}
// 隐藏指定弹窗
hidePopup(entityId) {
if (this.popups.has(entityId)) {
const popup = this.popups.get(entityId);
popup.element.classList.remove('active');
if (this.activePopup && this.activePopup.entity.id === entityId) {
this.activePopup = null;
}
logEvent(`隐藏弹窗: ${popup.entity.name || "未命名实体"}`);
}
}
// 隐藏所有弹窗
hideAllPopups() {
for (const [entityId, popup] of this.popups) {
popup.element.classList.remove('active');
}
this.activePopup = null;
logEvent("隐藏所有弹窗");
}
// 移除指定实体的弹窗
removePopup(entityId) {
if (this.popups.has(entityId)) {
const popup = this.popups.get(entityId);
if (popup.element.parentNode) {
popup.element.parentNode.removeChild(popup.element);
}
this.popups.delete(entityId);
if (this.activePopup && this.activePopup.entity.id === entityId) {
this.activePopup = null;
}
logEvent(`移除弹窗: ${popup.entity.name || "未命名实体"}`);
}
}
// 清除所有弹窗
clearAllPopups() {
for (const [entityId, popup] of this.popups) {
if (popup.element.parentNode) {
popup.element.parentNode.removeChild(popup.element);
}
}
this.popups.clear();
this.activePopup = null;
logEvent("清除所有弹窗");
}
// 销毁组件
destroy() {
this.clearAllPopups();
if (this.handler) {
this.handler.destroy();
}
if (this.preUpdateHandler) {
this.viewer.scene.preUpdate.removeEventListener(this.preUpdateHandler);
}
logEvent("销毁弹窗组件");
}
}
// 创建弹窗组件实例
const popupManager = new CesiumPopup(viewer);
// 添加示例扎点
function addDemoEntities() {
// 示例1 - 纽约
const entity1 = viewer.entities.add({
name: "纽约市",
position: Cesium.Cartesian3.fromDegrees(-74.00597, 40.71427),
billboard: {
image: 'https://ecmb.bdimg.com/tam-ogel/-440932281_-336120762_88_88.png',
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
width: 48,
height: 48,
color: Cesium.Color.ROYALBLUE
},
popupContent: `
<h3>纽约市</h3>
<p><strong>人口:</strong> 8,400,000</p>
<p><strong>特色:</strong> 自由女神像、中央公园、时代广场</p>
<p>美国最大城市和金融中心,被称为"不夜城"。</p>
`
});
// 示例2 - 华盛顿
const entity2 = viewer.entities.add({
name: "华盛顿特区",
position: Cesium.Cartesian3.fromDegrees(-77.03687, 38.90719),
billboard: {
image: 'https://ecmb.bdimg.com/tam-ogel/-440932281_-336120762_88_88.png',
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
width: 48,
height: 48,
color: Cesium.Color.CRIMSON
},
popupContent: `
<h3>华盛顿特区</h3>
<p><strong>人口:</strong> 705,749</p>
<p><strong>特色:</strong> 白宫、国会大厦、林肯纪念堂</p>
<p>美国首都,政治中心和许多国家纪念馆的所在地。</p>
`
});
// 示例3 - 波士顿
const entity3 = viewer.entities.add({
name: "波士顿",
position: Cesium.Cartesian3.fromDegrees(-71.05888, 42.36025),
billboard: {
image: 'https://ecmb.bdimg.com/tam-ogel/-440932281_-336120762_88_88.png',
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
width: 48,
height: 48,
color: Cesium.Color.FORESTGREEN
},
popupContent: `
<h3>波士顿</h3>
<p><strong>人口:</strong> 675,647</p>
<p><strong>特色:</strong> 自由之路、哈佛大学、芬威公园</p>
<p>美国最古老的城市之一,以其丰富的历史和高等教育机构闻名。</p>
`
});
// 示例4 - 迈阿密
const entity4 = viewer.entities.add({
name: "迈阿密",
position: Cesium.Cartesian3.fromDegrees(-80.19179, 25.76168),
billboard: {
image: 'https://ecmb.bdimg.com/tam-ogel/-440932281_-336120762_88_88.png',
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
width: 48,
height: 48,
color: Cesium.Color.GOLD
},
popupContent: `
<h3>迈阿密</h3>
<p><strong>人口:</strong> 463,347</p>
<p><strong>特色:</strong> 南海滩、艺术装饰街区、温暖气候</p>
<p>国际金融和文化中心,以其拉丁文化影响和海滩闻名。</p>
`
});
// 示例5 - 芝加哥
const entity5 = viewer.entities.add({
name: "芝加哥",
position: Cesium.Cartesian3.fromDegrees(-87.62980, 41.87811),
billboard: {
image: 'https://ecmb.bdimg.com/tam-ogel/-440932281_-336120762_88_88.png',
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
width: 48,
height: 48,
color: Cesium.Color.PURPLE
},
popupContent: function() {
const date = new Date();
return `
<h3>芝加哥</h3>
<p><strong>访问时间:</strong> ${date.toLocaleTimeString()}</p>
<p><strong>特色:</strong> 威利斯大厦、千禧公园、深盘披萨</p>
<p>美国第三大城市,以其建筑、文化和美食闻名。</p>
`;
}
});
// 将视角移动到实体范围
viewer.zoomTo(viewer.entities);
return [entity1, entity2, entity3, entity4, entity5];
}
// 添加随机扎点
function addRandomPoint() {
const longitude = -180 + Math.random() * 360;
const latitude = -90 + Math.random() * 180;
const entity = viewer.entities.add({
name: "随机位置",
position: Cesium.Cartesian3.fromDegrees(longitude, latitude),
billboard: {
image: 'https://ecmb.bdimg.com/tam-ogel/-440932281_-336120762_88_88.png',
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
width: 48,
height: 48,
color: Cesium.Color.PURPLE
},
popupContent: `
<h3>随机位置</h3>
<p><strong>经度:</strong> ${longitude.toFixed(4)}°</p>
<p><strong>纬度:</strong> ${latitude.toFixed(4)}°</p>
<p>这是一个随机生成的位置点。</p>
`
});
updateStatus(`已添加随机扎点: ${longitude.toFixed(2)}°, ${latitude.toFixed(2)}°`);
logEvent(`添加随机扎点: ${longitude.toFixed(2)}°, ${latitude.toFixed(2)}°`);
return entity;
}
// 清除所有实体
function clearAllEntities() {
viewer.entities.removeAll();
popupManager.clearAllPopups();
updateStatus("已清除所有扎点和弹窗");
logEvent("清除所有实体和弹窗");
}
// 添加示例实体
const demoEntities = addDemoEntities();
// 添加控制按钮事件
document.getElementById('addPoint').addEventListener('click', function() {
addRandomPoint();
});
document.getElementById('clearAll').addEventListener('click', function() {
clearAllEntities();
});
// 添加说明
console.log("Cesium弹窗组件已初始化");
console.log("- 点击地图上的标记点查看弹窗");
console.log("- 弹窗互斥显示,一次只显示一个");
console.log("- 移动或缩放地图时,弹窗会跟随扎点实时移动");
console.log("- 点击弹窗右上角的关闭按钮可以关闭弹窗");
updateStatus("就绪 - 点击地图上的扎点查看信息");
logEvent("Cesium弹窗组件初始化完成");
</script>
</body>
</html>
结语
本文介绍的Cesium弹窗组件提供了完整的地图信息展示解决方案,既保留了Cesium强大的地理可视化能力,又弥补了其在用户交互方面的不足。通过灵活的配置选项和丰富的扩展接口,开发者可以快速构建出专业级的地图应用界面。
该组件已在实际项目中得到验证,能够有效提升用户体验和操作效率。无论是简单的信息展示还是复杂的交互操作,都能提供稳定可靠的支持。
2547

被折叠的 条评论
为什么被折叠?



