首页 / 专栏 / Cesium / Cesium ScreenSpaceEventHandler 深度教程

Cesium ScreenSpaceEventHandler 深度教程

  • 发布时间: 2026-02-27 10:30:52
  • 相关标签: cesium screenspaceeventhandler 鼠标事件
  • 简介: 你想要深入理解 Cesium 中的 `ScreenSpaceEventHandler` 这个核心类,掌握它如何处理用户交互事件并应用到 Cesium 三维场景开发中,我会从基础概念、核心用法到实战案例,全面讲解这个类的使用方法。

一、核心概念理解

ScreenSpaceEventHandler 是 Cesium 中处理屏幕空间用户输入事件的核心类,它可以监听 HTML Canvas 元素(Cesium 地球容器)上的鼠标、触摸等交互行为,并绑定自定义处理函数。

1.1 核心作用

  • 监听鼠标(点击、移动、滚轮、按下/松开)、触摸(单点、双点、长按)等事件
  • 支持键盘修饰键(Ctrl、Shift、Alt)与鼠标/触摸事件的组合监听
  • 统一处理 PC 端鼠标事件和移动端触摸事件,无需单独适配
  • 提供事件销毁、移除等生命周期管理方法,避免内存泄漏

1.2 关键属性说明

属性 作用 默认值
mouseEmulationIgnoreMilliseconds 触摸事件后禁用鼠标模拟事件的时长 800ms
touchHoldDelayMilliseconds 触摸长按判定的延迟时长 1500ms

二、基础使用流程

2.1 核心步骤(通用模板)

// 1. 获取Cesium容器(Canvas元素)
const canvas = viewer.canvas;

// 2. 创建事件处理器实例
const handler = new Cesium.ScreenSpaceEventHandler(canvas);

// 3. 绑定事件处理函数
handler.setInputAction(
  (event) => {
    // 事件处理逻辑
    console.log('事件触发,位置:', event.position);
  },
  Cesium.ScreenSpaceEventType.LEFT_CLICK, // 事件类型
  Cesium.KeyboardEventModifier.CTRL // 可选:键盘修饰键
);

// 4. 销毁处理器(页面卸载/不再需要时)
// 推荐写法:避免重复销毁报错
handler = handler && handler.destroy();

2.2 核心方法详解

(1)setInputAction(绑定事件)

最核心的方法,用于绑定事件类型和处理函数:

// 示例1:监听左键单击
handler.setInputAction(
  (event) => {
    // event.position 是屏幕笛卡尔坐标(Cartesian2)
    const windowPosition = event.position;
    // 将屏幕坐标转换为世界坐标(地形/模型表面)
    const cartesian = viewer.scene.globe.pick(
      viewer.camera.getPickRay(windowPosition),
      viewer.scene
    );
    if (cartesian) {
      const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
      const lon = Cesium.Math.toDegrees(cartographic.longitude);
      const lat = Cesium.Math.toDegrees(cartographic.latitude);
      const height = cartographic.height;
      console.log('点击位置经纬度:', lon, lat, height);
    }
  },
  Cesium.ScreenSpaceEventType.LEFT_CLICK
);

// 示例2:监听Shift+右键单击
handler.setInputAction(
  (event) => {
    console.log('Shift+右键单击触发');
  },
  Cesium.ScreenSpaceEventType.RIGHT_CLICK,
  Cesium.KeyboardEventModifier.SHIFT
);

(2)removeInputAction(移除事件)

移除已绑定的事件处理函数:

// 移除左键单击事件
handler.removeInputAction(
  Cesium.ScreenSpaceEventType.LEFT_CLICK
);

// 移除带修饰键的事件
handler.removeInputAction(
  Cesium.ScreenSpaceEventType.RIGHT_CLICK,
  Cesium.KeyboardEventModifier.SHIFT
);

(3)getInputAction(获取绑定的事件函数)

const clickAction = handler.getInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
if (clickAction) {
  console.log('已绑定左键单击处理函数');
}

(4)destroy / isDestroyed(销毁/判断销毁状态)

// 安全销毁
if (!handler.isDestroyed()) {
  handler.destroy();
}
// 或者简化写法
handler = handler && handler.destroy();

三、常见事件类型与回调参数

3.1 基础事件类型(ScreenSpaceEventType)

事件类型 触发条件 回调参数类型 核心参数
LEFT_CLICK 鼠标左键单击/触摸单点点击 PositionedEvent position(Cartesian2)
LEFT_DOUBLE_CLICK 鼠标左键双击 PositionedEvent position
LEFT_DOWN 鼠标左键按下/触摸按下 PositionedEvent position
LEFT_UP 鼠标左键松开/触摸松开 PositionedEvent position
RIGHT_CLICK 鼠标右键单击 PositionedEvent position
MIDDLE_CLICK 鼠标中键单击 PositionedEvent position
MOUSE_MOVE 鼠标移动/触摸滑动 MotionEvent startPosition, endPosition
WHEEL 鼠标滚轮滚动 WheelEvent delta(滚动值)
PINCH 双指缩放 TwoPointMotionEvent position1/2, previousPosition1/2

3.2 实战案例:双指缩放监听

handler.setInputAction(
  (event) => {
    // 获取双指当前位置和上一位置
    const pos1 = event.position1;
    const pos2 = event.position2;
    const prevPos1 = event.previousPosition1;
    const prevPos2 = event.previousPosition2;
    
    // 计算缩放比例
    const currentDistance = Cesium.Cartesian2.distance(pos1, pos2);
    const previousDistance = Cesium.Cartesian2.distance(prevPos1, prevPos2);
    const scale = currentDistance / previousDistance;
    
    console.log('双指缩放比例:', scale);
    // 可根据缩放比例自定义相机行为
    viewer.camera.zoomIn((1 - scale) * 100);
  },
  Cesium.ScreenSpaceEventType.PINCH
);

四、高级应用场景

4.1 场景1:鼠标拾取实体

// 监听鼠标移动,高亮拾取的实体
handler.setInputAction(
  (event) => {
    // 获取鼠标位置对应的实体
    const pickedObject = viewer.scene.pick(event.endPosition);
    if (Cesium.defined(pickedObject) && pickedObject.id) {
      // 高亮实体(修改样式)
      pickedObject.id.color = Cesium.Color.YELLOW;
      // 记录上一个高亮的实体,恢复样式
      if (window.lastPickedEntity) {
        window.lastPickedEntity.color = Cesium.Color.RED;
      }
      window.lastPickedEntity = pickedObject.id;
    } else if (window.lastPickedEntity) {
      // 鼠标离开实体,恢复样式
      window.lastPickedEntity.color = Cesium.Color.RED;
      window.lastPickedEntity = undefined;
    }
  },
  Cesium.ScreenSpaceEventType.MOUSE_MOVE
);

4.2 场景2:自定义相机控制

// 监听鼠标拖拽(左键按下+移动)实现自定义平移
let isDragging = false;
let startPosition;

// 监听左键按下
handler.setInputAction(
  (event) => {
    isDragging = true;
    startPosition = event.position;
  },
  Cesium.ScreenSpaceEventType.LEFT_DOWN
);

// 监听鼠标移动
handler.setInputAction(
  (event) => {
    if (isDragging) {
      const endPosition = event.endPosition;
      // 计算鼠标移动偏移量
      const deltaX = endPosition.x - startPosition.x;
      const deltaY = endPosition.y - startPosition.y;
      
      // 自定义相机平移
      viewer.camera.moveRight(-deltaX * 10);
      viewer.camera.moveUp(deltaY * 10);
      
      startPosition = endPosition;
    }
  },
  Cesium.ScreenSpaceEventType.MOUSE_MOVE
);

// 监听左键松开
handler.setInputAction(
  () => {
    isDragging = false;
  },
  Cesium.ScreenSpaceEventType.LEFT_UP
);

4.3 场景3:触摸长按事件模拟

let touchHoldTimer = null;

// 监听触摸按下
handler.setInputAction(
  (event) => {
    // 启动长按计时器
    touchHoldTimer = setTimeout(() => {
      console.log('触摸长按触发,位置:', event.position);
      // 执行长按逻辑(如显示弹窗、创建标记等)
      viewer.entities.add({
        position: viewer.scene.globe.pick(
          viewer.camera.getPickRay(event.position),
          viewer.scene
        ),
        point: {
          pixelSize: 10,
          color: Cesium.Color.RED
        }
      });
    }, Cesium.ScreenSpaceEventHandler.touchHoldDelayMilliseconds);
  },
  Cesium.ScreenSpaceEventType.LEFT_DOWN
);

// 监听触摸松开,清除计时器
handler.setInputAction(
  () => {
    clearTimeout(touchHoldTimer);
  },
  Cesium.ScreenSpaceEventType.LEFT_UP
);

五、最佳实践与注意事项

5.1 内存泄漏防范

  • 必须销毁处理器:在组件卸载、页面关闭时,调用 destroy() 方法
  • 避免重复创建:确保同一容器只创建一个 ScreenSpaceEventHandler 实例
  • 及时移除事件:不再需要的事件,调用 removeInputAction 移除

5.2 跨端兼容

  • 移动端触摸事件会自动模拟鼠标事件,可通过 mouseEmulationIgnoreMilliseconds 调整忽略时长
  • 双指操作(缩放、旋转)优先使用 PINCH/TWIST 事件,而非自定义计算

5.3 性能优化

  • 鼠标移动事件(MOUSE_MOVE)触发频率高,避免在回调中执行复杂计算
  • 可通过节流函数限制高频事件的执行次数:
    function throttle(fn, delay) {
      let lastTime = 0;
      return (...args) => {
        const now = Date.now();
        if (now - lastTime > delay) {
          fn(...args);
          lastTime = now;
        }
      };
    }
    
    // 节流处理鼠标移动事件(每100ms执行一次)
    handler.setInputAction(
      throttle((event) => {
        // 处理逻辑
      }, 100),
      Cesium.ScreenSpaceEventType.MOUSE_MOVE
    );
    

总结

关键点回顾

  1. ScreenSpaceEventHandler 是 Cesium 处理屏幕交互的核心类,支持鼠标/触摸/键盘组合事件,需绑定到 Canvas 元素使用。
  2. 核心方法是 setInputAction(绑定事件)、removeInputAction(移除事件)、destroy(销毁实例),使用时需注意生命周期管理。
  3. 不同事件类型对应不同的回调参数结构,可通过屏幕坐标转换实现实体拾取、坐标获取等核心功能,开发时需注意跨端兼容和性能优化。

同分类推荐