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);
});