Cesium 快速入门(十三)事件系统

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);
});
相关推荐
用户47949283569154 分钟前
面试官:为什么很多格式化工具都会在行尾额外空出一行
前端
知识分享小能手5 分钟前
Vue3 学习教程,从入门到精通,Vue3 中使用 Axios 进行 Ajax 请求的语法知识点与案例代码(23)
前端·javascript·vue.js·学习·ajax·vue·vue3
一大树5 分钟前
首屏白屏的处理方案~嗖得一下
前端
小old弟7 分钟前
🤔同时发送100个请求?!手撕,并发请求⌨️
前端
533_12 分钟前
[echarts] 更新数据
前端·javascript·echarts
excel12 分钟前
理解 JavaScript 中的迭代器协议与中断行为:for...of vs for...in
前端
幻雨様14 分钟前
UE5多人MOBA+GAS 番外篇:同时造成多种类型伤害,以各种属性值的百分比来应用伤害(版本二)
java·前端·ue5
讨厌吃蛋黄酥18 分钟前
利用Mock实现前后端联调的解决方案
前端·javascript·后端
zzywxc78740 分钟前
在处理大数据列表渲染时,React 虚拟列表是提升性能的关键技术,但在实际实现中常遇到渲染抖动和滚动定位偏移等问题。
前端·javascript·人工智能·深度学习·react.js·重构·ecmascript
Hello.Reader1 小时前
Rust → WebAssembly 的性能剖析全指南
前端·rust·wasm