Skip to content

事件应用

Cesium 中根据事件的类型、用途,可以将事件分为三大类:

事件描述
鼠标操作事件ScreenSpaceEventHandler鼠标左键、鼠标中键、鼠标右键操作
通用Event事件无具体类该类通常在容器类内部实例化,并作为某个属性的类型被直接调用
相机控制事件ScreenSpaceCameraController该类通过与 CameraEventType 类配合实现相机的控制

鼠标事件

Cesium 中鼠标事件可以分为两个过程:

  1. 传递 viewer.canvas 参数,实例化 ScreenSpaceEventHandler 类;
  2. 使用 setInputAction() 方法设置鼠标事件,在回调函数中获取鼠标信息;

鼠标事件创建

对 ScreenSpaceEventHandler 类进行实例化,注册事件和注销事件:

js
const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
const eventType = cesium.ScreenSpaceEventType.LEFT_CLICK;

// 注册事件
handler.setInputAction((e) => {
  console.log(e);
}, eventType);

// 注销事件
handler.removeInputAction(eventType);

上面代码中的事件类型直接采用了 ScreenSpaceEventType 中的常量,它包括以下几种:

  • 鼠标左键

    事件类型作用
    LEFT_CLICK左键单击
    LEFT_DOUBLE_CLICK左键双击
    LEFT_DOWN左键按下
    LEFT_UP左键弹起
  • 鼠标中键

    事件类型作用
    MIDDLE_CLICK中键单击
    MIDDLE_DOWN中键按下
    MIDDLE_UP中键弹起
  • 鼠标右键

    事件类型作用
    RIGHT_CLICK右键单击
    RIGHT_DOWN左键按下
    RIGHT_UP左键弹起
  • 双指触摸

    事件类型作用
    PINCH_START双指开始事件
    PINCH_END双指结束事件
    PINCH_MOVE双指更改事件
  • 其他鼠标事件

    事件类型作用
    MOUSE_MOVE鼠标移动事件
    WHEEL鼠标滚轮事件

要素拾取

假设应用场景比较复杂,存在 Entity、Primitive、Model、3D Tiles 等多种要素时,鼠标拾取时可能会拾取到多个元素,如果想拾取单一元素时,可以使用 instanceof 方法来判断。

js
const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
const eventType = cesium.ScreenSpaceEventType.LEFT_CLICK;

handler.setInputAction(e => {
  const picked = viewer.scene.pick(e.position);

  if (Cesium.defined(picked)) {
    if (picked.id && picked.id instanceof Cesium.Entity) {
      console.log("选中了Entity", picked);
    }
    if (picked.primitive instanceof Cesium.Primitive) {
      console.log("选中了Primitive", picked);
    }
    if (picked.primitive instanceof Cesium.Model) {
      console.log("选中了Model", picked);
    }
    if (picked instanceof Cesium.Cesium3DTileFeature) {
      console.log("选中了3DTiles", picked);
    }
  }
}, eventType);

获取鼠标坐标

js
const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
const eventType = cesium.ScreenSpaceEventType.LEFT_CLICK;

handler.setInputAction(e => {
  const pickPosition = viewer.scene.camera.pickEllipsoid(e.position);
  if (Cesium.defined(pickPosition)) {
    // 笛卡尔坐标转为弧度坐标
    const Cartographic = Cesium.Cartographic.fromCartesian(pickPosition);
    const lng = Cesium.Math.toDegrees(Cartographic.longitude);
    const lat = Cesium.Math.toDegrees(Cartographic.latitude);
    console.log({ lng, lat });
  }
}, eventType);
js
const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
const eventType = cesium.ScreenSpaceEventType.LEFT_CLICK;

handler.setInputAction(e => {
  // 根据屏幕位置获取摄像机射线
  const ray = viewer.camera.getPickRay(e.position);
  // 获取射线与地球表面的交点
  const cartesian = viewer.scene.globe.pick(ray, viewer.scene);
  if (Cesium.defined(cartesian)) {
    const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
    const lng = Cesium.Math.toDegrees(cartographic.longitude);
    const lat = Cesium.Math.toDegrees(cartographic.latitude);
    console.log({ lng, lat });
  }
}, eventType);

为图层添加事件

js
// 绘制多边形
const instance = new Cesium.GeometryInstance({
  id: "rectangle",
  geometry: new Cesium.RectangleGeometry({
    rectangle: Cesium.Rectangle.fromDegrees(-100.0, 30.0, -90.0, 40.0)
  }),
  attributes: {
    color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED)
  }
});

viewer.scene.primitives.add(
  new Cesium.Primitive({
    geometryInstances: instance,
    appearance: new Cesium.PerInstanceColorAppearance()
  })
);

const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
const eventType = cesium.ScreenSpaceEventType.LEFT_CLICK;

handler.setInputAction(e => {
  const pick = viewer.scene.pick(e.position);
  // 按照图层Id进行事件出发条件过滤
  if (Cesium.defined(pick) && pick.id === "rectangle") {
    console.log("Mouse clicked rectangle.");
  }
}, eventType);

相机事件

相机控制事件类 screenSpaceCameraController 不像鼠标事件相关类那样需要提前实例化。Cesium 在 Viewer类的实例化过程中,也实例化了其他很多类,其中就包括了ScreenSpaceCameraController 类,并把实例化结果赋值给了viewer.scene.screenSpaceCameraController。

所以,我们直接去操作 viewer.scene.screenSpaceCameraController 就可以了。

鼠标控制

通过鼠标控制相机的方式取决于 CameraEventType 的常量,包括以下几种:

  • 鼠标操作相机事件

    事件类型作用
    LEFT_DRAG按住鼠标左键,然后移动鼠标并释放按钮
    MIDDLE_DRAG按住鼠标中键,然后移动鼠标并释放按钮
    RIGHT_DRAG按住鼠标右键,然后移动鼠标并释放按钮
    PINCH触摸表面上的双指触摸
    WHEEL滚动鼠标中键

下面的示例,修改默认的鼠标事件,实现鼠标中键缩放,右键旋转。

js
const viewer = new Cesium.Viewer("cesiumContainer", {
  infoBox: false,
  baseLayerPicker: true
})

// 配置视图倾斜事件
viewer.scene.screenSpaceCameraController.tiltEventTypes = [
  // 鼠标右键拖动时触发视图的倾斜操作
  Cesium.CameraEventType.RIGHT_DRAG,
  // 双指触摸时触发视图的倾斜操作
  Cesium.CameraEventType.PINCH,
  // 按住 Ctrl 键的同时,鼠标左键拖动时触发视图的倾斜操作
  {
    eventType: Cesium.CameraEventType.LEFT_DRAG,
    mofifier: Cesium.KeyboardEventModifier.CTRL
  },
  // 按住 Ctrl 键的同时,鼠标右键拖动时触发视图的倾斜操作
  {
    eventType: Cesium.CameraEventType.RIGHT_DRAG,
    mofifier: Cesium.KeyboardEventModifier.CTRL
  }
]

// 配置视图缩放事件
viewer.scene.screenSpaceCameraController.zoomEventTypes = [
  // 按住滚轮中键可以缩放
  Cesium.CameraEventType.MIDDLE_DRAG,
  // 滚动滚轮中键可以缩放
  Cesium.CameraEventType.WHEEL,
  // 手指可缩放
  Cesium.CameraEventType.PINCH
]

键盘控制

通过操作键盘实现相机的漫游,比如前进、后退、向上、向下等。

Cesium 中实现键盘漫游主要通过调用相机的 moveForward、moveBackward、moveLeft、moveRight、moveUp、moveDown方法。

js
const viewer = new Cesium.Viewer("cesiumContainer", {
  infoBox: false,
  baseLayerPicker: true
})
const scene = viewer.scene
const canvas = viewer.canvas
const elliposid = scene.globe.ellipsoid

let startMousePosition: any
let mousePosition: any
const flags = {
  looking: false,
  moveForward: false,
  moveBackward: false,
  moveUp: false,
  moveDown: false,
  moveLeft: false,
  moveRight: false
}

function getFlagForKeyCode(code: string) {
  switch (code) {
    case "KeyW":
      return "moveForward"
    case "KeyS":
      return "moveBackward"
    case "KeyQ":
      return "moveUp"
    case "KeyE":
      return "moveDown"
    case "KeyD":
      return "moveRight"
    case "KeyA":
      return "moveLeft"
    default:
      return undefined
  }
}

document.addEventListener("keydown", e => {
  const flagName = getFlagForKeyCode(e.code)
  if (typeof flagName !== "undefined") {
    flags[flagName] = true
  }
})
document.addEventListener("keyup", e => {
  const flagName = getFlagForKeyCode(e.code)
  if (typeof flagName !== "undefined") {
    flags[flagName] = false
  }
})

viewer.clock.onTick.addEventListener((_e: any) => {
  const camera = viewer.camera

  if (flags.looking) {
    const width = canvas.width
    const height = canvas.height

    const x = (mousePosition.x - startMousePosition.x) / width
    const y = (mousePosition.y - startMousePosition.y) / height

    const lookFactor = 0.05
    camera.lookRight(x * lookFactor)
    camera.lookUp(y * lookFactor)
  }

  const cameraHeight = elliposid.cartesianToCartographic(
    camera.position
  ).height
  const moveRate = cameraHeight / 100.0

  if (flags.moveForward) {
    camera.moveForward(moveRate)
  }
  if (flags.moveBackward) {
    camera.moveBackward(moveRate)
  }
  if (flags.moveUp) {
    camera.moveUp(moveRate)
  }
  if (flags.moveDown) {
    camera.moveDown(moveRate)
  }
  if (flags.moveLeft) {
    camera.moveLeft(moveRate)
  }
  if (flags.moveRight) {
    camera.moveRight(moveRate)
  }
})

场景渲染事件

场景渲染事件主要包括以下四种:

事件作用
scene.preUpdate更新或呈现场景之前将引发的事件
scene.postUpdate场景更新后,以及渲染场景之前立即引发的事件
scene.preRender场景更新后,以及渲染场景之前将引发的事件
scene.postRender渲染场景后立即引发的事件

事件的添加和移除代码示例:

js
viewer.scene.preUpdate.addEventListener(callback)
viewer.scene.preUpdate.removeEventListener(callback)
js
// 1.更新或呈现场景之前将引发的事件
viewer.scene.preUpdate.addEventListener(callbackFunc)
viewer.scene.preUpdate.removeEventListener(callbackFunc)

// 2.场景更新后以及渲染场景之前立即引发的事件
viewer.scene.postUpdate.addEventListener(callbackFunc)
viewer.scene.postUpdate.removeEventListener(callbackFunc)

// 3.场景更新后以及渲染场景之前将引发的事件
viewer.scene.preRender.addEventListener(callbackFunc)
viewer.scene.preRender.removeEventListener(callbackFunc)

// 4.渲染场景后立即引发的事件
viewer.scene.postRender.addEventListener(callbackFunc)
viewer.scene.postRender.removeEventListener(callbackFunc)

// 需要回调的函数
function callbackFunc(e: any) {
  console.log(e)
}

Released under the MIT License.