Cesium 快速入门(十三)事件系统
看过的知识不等于学会。唯有用心总结、系统记录,并通过温故知新反复实践,才能真正掌握一二
作为一名摸爬滚打三年的前端开发,开源社区给了我饭碗,我也将所学的知识体系回馈给大家,助你少走弯路!
OpenLayers、Leaflet 快速入门 ,每周保持更新 2 个案例
Cesium 快速入门,每周保持更新 4 个案例
Cesium 快速入门(一)快速搭建项目
Cesium 快速入门(二)底图更换
Cesium 快速入门(三)Viewer:三维场景的"外壳"
Cesium 快速入门(四)相机控制完全指南
Cesium 快速入门(五)坐标系
Cesium 快速入门(六)实体类型介绍
Cesium 快速入门(七)材质详解
Cesium 快速入门(八)Primitive(图元)系统深度解析
Cesium 快速入门(九)Appearance(外观)系统深度解析
Cesium 快速入门(十) JulianDate(儒略日期)详解
Cesium 快速入门(十一)3D Tiles 大规模三维地理空间数据
Cesium 快速入门(十二)数据加载详解
Cesium 快速入门(十三)事件系统
事件学习
Cesium 的事件系统允许开发者监听和响应场景中的各类交互与状态变化,涵盖 相机操作、实体交互、屏幕输入 等核心场景
屏幕空间事件
屏幕空间事件是最常用的事件类型,用于处理用户在屏幕上的交互操作,如鼠标点击、移动、悬停等,使用 new Cesium.ScreenSpaceEventHandler(element)
,ScreenSpaceEventHandler 官方文档
鼠标交互事件
js
viewer.screenSpaceEventHandler.setInputAction(action, type, modifier);
参数 | 类型 | 描述 |
---|---|---|
action |
Function | 事件触发后执行的回调函数 |
type |
ScreenSpaceEventType | 事件类型(如鼠标左键点击、移动等) |
modifier |
KeyboardEventModifier | 可选,键盘事件修饰符, 如 shift, ctrl, alt 等 |
名字 | 类型 | 描述 |
---|---|---|
LEFT_DOWN |
number | 表示鼠标左键按下事件。 |
LEFT_UP |
number | 表示鼠标左键松开事件。 |
LEFT_CLICK |
number | 表示鼠标左键单击事件。 |
LEFT_DOUBLE_CLICK |
number | 表示鼠标左键双击事件。 |
RIGHT_DOWN |
number | 表示鼠标右键按下事件。 |
RIGHT_UP |
number | 表示鼠标右键松开事件。 |
RIGHT_CLICK |
number | 表示鼠标右键单击事件。 |
MIDDLE_DOWN |
number | 表示鼠标中键按下事件。 |
MIDDLE_UP |
number | 表示鼠标中键松开事件。 |
MIDDLE_CLICK |
number | 表示鼠标中键单击事件。 |
MOUSE_MOVE |
number | 表示鼠标移动事件。 |
WHEEL |
number | 表示鼠标滚轮事件。 |
PINCH_START |
number | 表示触摸表面上双指事件的开始。 |
PINCH_END |
number | 表示触摸表面上双指事件的结束。 |
PINCH_MOVE |
number | 表示触摸表面上双指事件的更改。 |
实战案例:鼠标点击获取经纬度
js
// 添加地图点击事件,点击后获取经纬度坐标
viewer.screenSpaceEventHandler.setInputAction(function (e) {
// 点击后获取点击位置的笛卡尔坐标
const cartesian = viewer.scene.pickPosition(e.position);
// 必须判断坐标是否有效(当点击天空盒等位置时会返回undefined)
if (Cesium.defined(cartesian)) {
// 笛卡尔坐标转弧度坐标
const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
// 弧度转度数并保留6位小数
const longitude = Cesium.Math.toDegrees(cartographic.longitude).toFixed(6);
const latitude = Cesium.Math.toDegrees(cartographic.latitude).toFixed(6);
const height = cartographic.height.toFixed(2);
console.log(`经度: ${longitude}, 纬度: ${latitude}, 高度: ${height}米`);
} else {
console.log("无法获取有效坐标(可能点击了天空或地形外区域)");
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
事件销毁
js
// 移除特定类型的事件监听
viewer.screenSpaceEventHandler.removeInputAction(
Cesium.ScreenSpaceEventType.LEFT_CLICK
);
// 完全销毁事件处理器(释放资源,避免内存泄漏)
viewer.screenSpaceEventHandler.destroy();
实体点击检测
判断点击到实体并获取实体信息:
js
// 添加实体
const circle = viewer.entities.add({
id: "circle",
name: "circle",
description: "This is circle",
position: Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883),
ellipse: {
semiMajorAxis: 1000,
semiMinorAxis: 1000,
material: Cesium.Color.RED.withAlpha(0.5),
outline: true,
outlineColor: Cesium.Color.WHITE,
},
});
// 定位到实体
viewer.zoomTo(circle);
// 添加鼠标点击事件
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function (e) {
// 使用scene.pick方法获取点击位置的实体
const pickedObject = viewer.scene.pick(e.position);
if (
Cesium.defined(pickedObject) &&
pickedObject.id instanceof Cesium.Entity
) {
const entity = pickedObject.id;
console.log("实体信息:", entity); // 输出实体信息
} else {
console.log("未点击到实体");
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
键盘事件
注意:Cesium 的键盘修饰符不能单独使用,需要配合鼠标事件。如需监听独立键盘事件,需使用原生 JavaScript 事件。
键盘修饰符列表
名字 | 描述 |
---|---|
SHIFT |
表示 shift 键被按住 |
CTRL |
表示 ctrl 键被按住 |
ALT |
表示 alt 键被按住 |
组合事件示例
js
// Shift+左键点击事件
viewer.screenSpaceEventHandler.setInputAction(
function (e) {
console.log("Shift+鼠标左键点击");
// 可以实现选择多个实体等功能
},
Cesium.ScreenSpaceEventType.LEFT_CLICK,
Cesium.KeyboardEventModifier.SHIFT
);
// Ctrl+Alt+右键点击事件
viewer.screenSpaceEventHandler.setInputAction(
function (e) {
console.log("Ctrl+Alt+鼠标右键点击");
},
Cesium.ScreenSpaceEventType.RIGHT_CLICK,
[Cesium.KeyboardEventModifier.CTRL, Cesium.KeyboardEventModifier.ALT]
);
相机事件
相机事件(如 RIGHT_DRAG 和 WHEEL)用于控制场景的交互行为(如旋转、缩放、倾斜)
事件类型与默认行为
注: 默认行为通过 ScreenSpaceCameraController
控制
事件类型 | 默认操作 | 适用视图模式 |
---|---|---|
CameraEventType.LEFT_DRAG |
旋转地球(3D)或平移(2D) | 3D/Columbus |
CameraEventType.RIGHT_DRAG |
缩放 | 所有模式 |
CameraEventType.MIDDLE_DRAG |
倾斜地球 | 3D/Columbus |
CameraEventType.WHEEL |
缩放 | 所有模式 |
CameraEventType.PINCH |
缩放/倾斜(触摸屏) | 所有模式 |
自定义相机交互行为
通过修改screenSpaceCameraController
属性自定义交互方式:
js
const cameraController = viewer.scene.screenSpaceCameraController;
// 示例1:修改旋转事件为右键拖拽(默认是左键)
cameraController.rotateEventTypes = [Cesium.CameraEventType.RIGHT_DRAG];
// 示例2:修改缩放事件为仅支持滚轮
cameraController.zoomEventTypes = [Cesium.CameraEventType.WHEEL];
// 示例3:配置倾斜事件支持多种方式
cameraController.tiltEventTypes = [
Cesium.CameraEventType.MIDDLE_DRAG, // 中键拖拽
{
eventType: Cesium.CameraEventType.LEFT_DRAG,
modifier: Cesium.KeyboardEventModifier.CTRL,
}, // Ctrl+左键拖拽
];
禁用默认相机交互
js
const cameraController = viewer.scene.screenSpaceCameraController;
// 禁用所有相机交互(完全控制场景)
cameraController.enableRotate = false; // 禁用旋转
cameraController.enableZoom = false; // 禁用缩放
cameraController.enableTilt = false; // 禁用倾斜
cameraController.enableTranslate = false; // 禁用平移
cameraController.enableLook = false; // 禁用视角控制
场景事件
场景事件包括相机移动、场景渲染等生命周期事件,用于监控场景状态变化。
相机状态事件
名字 | 描述 |
---|---|
moveStart |
获取将在摄像机开始移动时引发的事件 |
moveEnd |
获取当摄像机停止移动时将引发的事件 |
changed |
获取当摄像机位置或方向发生变化时将引发的事件 |
js
// 监听相机移动开始
viewer.camera.moveStart.addEventListener(() => {
console.log("相机开始移动");
});
// 监听相机移动结束、获取相机位置
viewer.camera.moveEnd.addEventListener(() => {
const positionCartographic = viewer.camera.positionCartographic;
let cameraPosition = {};
cameraPosition.y = Number(
Cesium.Math.toDegrees(positionCartographic.latitude).toFixed(6)
);
cameraPosition.x = Number(
Cesium.Math.toDegrees(positionCartographic.longitude).toFixed(6)
);
cameraPosition.z = Number(positionCartographic.height.toFixed(1));
cameraPosition.heading = Number(
Cesium.Math.toDegrees(viewer.camera.heading || -90).toFixed(1)
);
cameraPosition.pitch = Number(
Cesium.Math.toDegrees(viewer.camera.pitch || 0).toFixed(1)
);
cameraPosition.roll = Number(
Cesium.Math.toDegrees(viewer.camera.roll || 0).toFixed(1)
);
console.log("相机位置", cameraPosition);
});
// 监听相机位置或方向发生变化
viewer.camera.changed.addEventListener(() => {
console.log("相机位置或方向发生变化");
});
场景渲染事件
事件名称 | 描述 | 触发频率 |
---|---|---|
preUpdate |
场景更新前触发 | 每帧 |
postUpdate |
场景更新后触发 | 每帧 |
preRender |
场景渲染前触发 | 每帧 |
postRender |
场景渲染后触发 | 每帧 |
js
// 监听场景更新之前的事件
viewer.scene.preUpdate.addEventListener(() => {
console.log("场景更新之前");
});
// 监听场景更新之后的事件
viewer.scene.postUpdate.addEventListener(() => {
console.log("场景更新之后");
});
// 监听场景渲染之前的事件
viewer.scene.preRender.addEventListener(() => {
console.log("场景渲染之前");
});
// 监听场景渲染之后的事件
viewer.scene.postRender.addEventListener(() => {
console.log("场景渲染之后");
});
// 限制高频事件的处理频率(性能优化)
let lastUpdateTime = 0;
viewer.scene.postUpdate.addEventListener((scene, time) => {
// 每100ms处理一次,避免每帧处理影响性能
if (time - lastUpdateTime > 0.1) {
lastUpdateTime = time;
// 执行需要定期更新的逻辑
}
});
自定义事件系统
构建独立的事件总线,实现组件间解耦通信:
js
// 创建自定义事件总线
const eventBus = {
events: {},
emit(event, data) {
if (this.events[event]) {
this.events[event].forEach((fn) => fn(data));
}
},
on(event, callback) {
if (!this.events[event]) this.events[event] = [];
this.events[event].push(callback);
},
};
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
// 在实体点击时触发自定义事件
handler.setInputAction((click) => {
const picked = viewer.scene.pick(click.position);
if (picked && picked.id) {
eventBus.emit("entity-clicked", picked.id);
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
// 监听自定义事件
eventBus.on("entity-clicked", (entity) => {
console.log("实体被点击:", entity.name);
});