Skip to content

Cesium事件系统详解

Cesium 的事件系统允许开发者监听和响应场景中的各类交互与状态变化,涵盖 相机操作、实体交互、屏幕输入 等核心场景

核心概念与事件分类

事件类别描述典型应用场景
输入事件用户交互触发(鼠标、触摸、键盘)点击拾取、拖拽编辑、缩放控制
相机事件相机位置和姿态变化视域监控、场景联动
场景事件渲染循环和状态更新性能监控、动态效果
实体事件实体属性和状态变化数据驱动交互、状态同步
自定义事件应用级业务事件组件通信、跨模块协作

屏幕空间事件

屏幕空间事件是最常用的事件类型,用于处理用户在屏幕上的交互操作,如鼠标点击、移动、悬停等,使用 new Cesium.ScreenSpaceEventHandler(element),ScreenSpaceEventHandler 官方文档

鼠标交互事件

js
viewer.screenSpaceEventHandler.setInputAction(action, type, modifier);
参数类型描述
actionFunction事件触发后执行的回调函数
typeScreenSpaceEventType事件类型(如鼠标左键点击、移动等)
modifierKeyboardEventModifier可选,键盘事件修饰符, 如 shift, ctrl, alt 等
名字类型描述
LEFT_DOWNnumber表示鼠标左键按下事件。
LEFT_UPnumber表示鼠标左键松开事件。
LEFT_CLICKnumber表示鼠标左键单击事件。
LEFT_DOUBLE_CLICKnumber表示鼠标左键双击事件。
RIGHT_DOWNnumber表示鼠标右键按下事件。
RIGHT_UPnumber表示鼠标右键松开事件。
RIGHT_CLICKnumber表示鼠标右键单击事件。
MIDDLE_DOWNnumber表示鼠标中键按下事件。
MIDDLE_UPnumber表示鼠标中键松开事件。
MIDDLE_CLICKnumber表示鼠标中键单击事件。
MOUSE_MOVEnumber表示鼠标移动事件。
WHEELnumber表示鼠标滚轮事件。
PINCH_STARTnumber表示触摸表面上双指事件的开始。
PINCH_ENDnumber表示触摸表面上双指事件的结束。
PINCH_MOVEnumber表示触摸表面上双指事件的更改。

实战案例:鼠标点击获取经纬度

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