'# DEJA_VU3D - Cesium功能集 之 94-广告版Billboard(标绘+编辑)
一、背景与问题
在三维地理信息系统(GIS)开发中,广告版Billboard是一种常见的需求场景。传统方案往往需要在Cesium中通过Billboard组件实现静态标记,但随着业务需求升级,需要支持动态编辑、交互式操作以及广告内容的实时更新。
现有方案存在以下痛点:
- 无法实现复杂的编辑操作(如拖动、缩放、内容修改)
- 缺乏对广告内容的动态更新机制
- 未考虑性能优化与安全防护
- 未提供完整的编辑流程闭环
本篇文章将深入解析广告版Billboard的实现原理,结合实际开发场景,探讨其适用场景与技术边界。
二、基本原理
Cesium的Billboard组件本质上是基于ScreenSpaceCameraController的2D渲染机制。其核心原理是通过以下步骤实现广告版功能:
- 坐标系转换:将地理坐标转换为屏幕坐标
- 渲染机制:通过
ScreenSpaceCameraController保持方向一致性 - 事件绑定:注册点击、拖拽等交互事件
- 状态管理:维护广告内容的动态状态
关键特性:
- 可视化组件与数据模型的分离
- 基于事件驱动的交互机制
- 动态内容更新的异步处理
三、环境准备
# 安装Cesium
npm install cesium// 基础HTML结构
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Ad Billboard</title>
<script src="https://unpkg.com/cesium@1.105.1/Build/Cesium/Cesium.js"></script>
<link href="https://unpkg.com/cesium@1.105.1/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
</head>
<body>
<div id="cesiumContainer"></div>
<script>
// 初始化Cesium Viewer
Cesium.Ion.defaultAccessToken = 'YOUR_TOKEN';
const viewer = new Cesium.Viewer('cesiumContainer', {
terrain: Cesium.Terrain.fromWorldTerrain()
});
</script>
</body>
</html>四、核心实现
1. 基础Billboard创建
const billboard = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883),
billboard: {
image: 'https://example.com/icon.png',
height: 32,
width: 32,
rotation: Cesium.Math.toRadians(45)
},
label: {
text: '广告位1',
font: '14px sans-serif',
backgroundColor: Cesium.Color.WHITE.withAlpha(0.8)
}
});关键点解释:
billboard属性控制图标样式label属性实现文本标注rotation控制图标旋转角度
2. 编辑功能实现
function createEditableBillboard(position, content) {
const entity = viewer.entities.add({
position: position,
billboard: {
image: 'https://example.com/edit-icon.png',
height: 32,
width: 32
},
label: {
text: content,
font: '14px sans-serif'
},
show: true
});
// 添加点击事件
viewer.dataSources.add(
new Cesium.CustomDataSource({
name: 'Ad Billboard',
display: false,
is3D: true
})
);
return entity;
}关键点解释:
- 使用
CustomDataSource管理实体集合 - 通过
show属性控制可见性 - 实现离线编辑的显示控制
3. 动态更新机制
function updateBillboardContent(entity, newContent) {
const currentLabel = entity.label;
if (currentLabel) {
currentLabel.text = newContent;
currentLabel.font = '16px sans-serif';
currentLabel.backgroundColor = Cesium.Color.RED.withAlpha(0.7);
}
}关键点解释:
- 通过修改
label属性实现内容更新 - 使用
backgroundColor控制视觉反馈 - 支持动态样式切换
五、完整案例
1. 广告位管理系统案例
<!-- 广告位管理界面 -->
<div id="controls">
<input type="text" id="adContent" placeholder="输入广告内容">
<button id="addBtn">添加广告位</button>
<button id="editBtn">编辑选中广告位</button>
</div>
<script>
let selectedEntity = null;
// 添加广告位
document.getElementById('addBtn').addEventListener('click', () => {
const content = document.getElementById('adContent').value;
if (!content) return;
const position = Cesium.Cartesian3.fromDegrees(
-75.59777 + Math.random() * 10,
40.03883 + Math.random() * 10
);
selectedEntity = createEditableBillboard(position, content);
document.getElementById('adContent').value = '';
});
// 编辑广告位
document.getElementById('editBtn').addEventListener('click', () => {
if (!selectedEntity) return;
const newContent = prompt('请输入新广告内容', selectedEntity.label.text);
if (newContent) {
updateBillboardContent(selectedEntity, newContent);
}
});
</script>完整案例说明:
- 包含添加和编辑功能的完整界面
- 支持动态内容更新
- 提供用户交互控制
六、源码解析
1. Billboard渲染机制
viewer.scene.postRender.addEventListener(() => {
const camera = viewer.camera;
const billboards = viewer.entities.entities;
for (let i = 0; i < billboards.length; i++) {
const entity = billboards[i];
if (!entity.billboard) continue;
const position = entity.position;
const rotation = entity.billboard.rotation;
// 计算屏幕坐标
const screenPosition = Cesium.Ellipsoid.WGS84.cartesianToCartesian(
position,
Cesium.Ellipsoid.WGS84,
new Cesium.Cartesian3()
);
// 绘制逻辑
// ...
}
});关键点解释:
- 使用
postRender事件保证渲染顺序 - 转换坐标系进行屏幕绘制
- 通过
rotation实现动态旋转
2. 交互事件处理
viewer.scene.postRender.addEventListener(() => {
const camera = viewer.camera;
const billboards = viewer.entities.entities;
for (let i = 0; i < billboards.length; i++) {
const entity = billboards[i];
if (!entity.billboard) continue;
const position = entity.position;
const screenPosition = Cesium.Ellipsoid.WGS84.cartesianToCartesian(
position,
Cesium.Ellipsoid.WGS84,
new Cesium.Cartesian3()
);
// 检测点击事件
if (isClickDetected(screenPosition)) {
console.log('检测到点击:', entity.label.text);
selectedEntity = entity;
}
}
});关键点解释:
- 通过屏幕坐标检测交互
- 实现点击事件的实时响应
- 保持状态同步
七、进阶使用
1. 状态持久化
function saveBillboardState(entity) {
const state = {
position: entity.position,
content: entity.label.text,
style: entity.billboard
};
// 保存到本地存储或服务器
localStorage.setItem('adBillboard', JSON.stringify(state));
}2. 动态样式切换
function toggleStyle(entity) {
const currentStyle = entity.billboard;
const newStyle = {
image: currentStyle.image === 'https://example.com/edit-icon.png'
? 'https://example.com/normal-icon.png'
: 'https://example.com/edit-icon.png',
height: currentStyle.height + 4,
width: currentStyle.width + 4
};
entity.billboard = newStyle;
}3. 安全防护机制
function sanitizeContent(content) {
return content.replace(/<script.*?>[\s\S]*?<\/script>/gi, '');
}八、性能与工程实践
1. 性能优化
- 批处理渲染:使用
CustomDataSource管理实体集合 - 减少DOM操作:批量更新内容而非频繁重绘
- 缓存计算结果:预计算屏幕坐标
2. 异常处理
try {
// 可能引发异常的代码
} catch (error) {
console.error('广告位操作异常:', error);
// 异常处理逻辑
}3. 安全防护
- 对用户输入内容进行HTML转义
- 限制特殊字符的输入
- 使用内容安全策略(CSP)
九、常见问题与踩坑
1. 位置不显示的常见问题
// 错误示例
const position = new Cesium.Cartesian3(123, 456, 789); // 错误坐标格式正确做法:
const position = Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883);2. 事件未触发的常见问题
// 错误示例
viewer.entities.entities.addEventListener('click', () => { /* ... */ });正确做法:
viewer.scene.postRender.addEventListener(() => {
// 点击检测逻辑
});3. 性能瓶颈问题
// 错误示例:频繁的postRender回调
viewer.scene.postRender.addEventListener(() => {
// 频繁的计算和绘制
});优化方案:
let isDirty = false;
viewer.scene.postRender.addEventListener(() => {
if (isDirty) {
// 执行更新逻辑
isDirty = false;
}
});十、最佳实践
- 使用CustomDataSource:集中管理实体集合
- 分层处理:区分静态与动态内容
- 状态管理:保持数据模型与视图的同步
- 安全防护:对用户输入内容进行严格校验
- 性能优化:使用缓存和批处理机制
十一、总结
广告版Billboard在Cesium中实现需要综合考虑渲染机制、交互设计、状态管理和性能优化。通过合理使用Billboard组件,结合事件驱动模型,可以实现复杂的编辑功能和动态内容更新。
实际开发中需要根据业务需求选择合适的实现方式:
- 适用场景:需要动态更新的广告位管理、实时信息展示
- 不适用场景:对性能要求极高的场景、需要复杂3D交互的场景
本方案通过完整的代码示例和深入分析,为开发者提供了可复用的实现框架,同时指出了常见错误和优化方向,有助于在实际项目中正确应用这一技术。