Cesium开发工具篇 | 02事件应用

无论是前端系统,还是二维/三维GIS应用系统,都离不开各种事件的应用,尤其是鼠标的单击、双击事件。 Cesium 根据事件的类型、用途,将事件应用分成了三大类。一种是以鼠标操作(左键、中键、右键操作等)为主的 ScreenSpaceEventHandler 类,另一种是通用的事件类 Event ,该类通常在容器类内部实例化,并作为某个属性的类型直接被调用,比如 viewer.clock.onTick、viewer.selectedEntityChanged、camera.moveStart、camera.moveEnd、scene.preRender、cesium3DTileset.allTilesLoaded等这些属性都是 Event 类型;最后一种则是相机控制方面的事件类 screenSpaceCameraController,该类通过与CameraEventType类配合实现相机的控制。下面我主要介绍几个比较常用的事件应用。

鼠标事件

鼠标事件可以说是GIS系统里面关于事件应用最常用的一个了,点击地图上的某一个 graphic,并获取其属性信息,就是鼠标事件应用最熟悉的一个场景了。Cesium为实现这一功能,分成了两个过程。首先,传递viewer.canvas参数实例化ScreenSpaceEventHandler类,比如实例化后的名称为handler;其次,为 handler 注册鼠标事件的监听;最后,在监听事件的回调方法中获取 event.position ,并将其作为参数执行scene.pick 方法获取对应的选中对象。

1)ScreenSpaceEventHandler
对 ScreenSpaceEventHandler 类进行实例化,注册事件、注销事件代码如下:

   var handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
    let eventType = Cesium.ScreenSpaceEventType.LEFT_CLICK;
    //注册事件
    handler.setInputAction((event) => {
      console.log(event);
    }, eventType);

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

上面代码中的事件类型 eventType 直接采用了 ScreenSpaceEventType 中的常量,示例中注册鼠标左击事件。根据实际场景需求,eventType 的值可按需赋值,包括以下几种:

2)要素拾取
假如应用场景是点击要素获取其属性信息,这个时候就需要在鼠标左键的注册事件中获取 event 结果,核心代码如下:

var picked = viewer.scene.pick(event.position);

这个时候就可以根据获取到的对象类型进行操作了。

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

3)Entity选择
Cesium 针对于通过 Entity 方式添加的几何图形,提供了一个非常方便的属性selectedEntityChanged(viewer类事件类型的属性)来帮助我们获取选中的Entity,通过这个属性,用户无需再写注册鼠标事件了。示例代码如下:

    viewer.selectedEntityChanged.addEventListener(function (entity) {
      console.log(entity.id);
    });

在某些场景中,我们可能需要跟踪某一辆车或某一个人员,这是我们可以把车辆或人员Entity赋给viewer.trackedEntity,相机就会自动跟踪你绑定的Entity了。实际场景中,我们并不是始终跟踪某一个车辆,有时需要切换到另一个车辆,当你切换正在跟踪的车辆时,其实我们触发了viewer.trackedEntityChanged事件,这样我们就可以在此事件中实时获取车辆行驶状态了。

    viewer.trackedEntityChanged.addEventListener(function (entity) {
      console.log(entity.id);
    });

相机事件

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

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

其中,鼠标的默认操作如下:

下面我们修改默认的鼠标操作,实现中键缩放、右键旋转。核心代码如下:

    viewer.scene.screenSpaceCameraController.tiltEventTypes = [
      Cesium.CameraEventType.RIGHT_DRAG,
      Cesium.CameraEventType.PINCH,
      {
        eventType: Cesium.CameraEventType.LEFT_DRAG,
        modifier: Cesium.KeyboardEventModifier.CTRL,
      },
      {
        eventType: Cesium.CameraEventType.RIGHT_DRAG,
        modifier: Cesium.KeyboardEventModifier.CTRL,
      },
    ];

    viewer.scene.screenSpaceCameraController.zoomEventTypes = [
      Cesium.CameraEventType.MIDDLE_DRAG,
      Cesium.CameraEventType.WHEEL,
      Cesium.CameraEventType.PINCH,
    ];

2)通过键盘控制
主要是通过操作键盘实现相机的漫游,比如前进、后退、向上、向下等等,是不是感觉自己在玩穿越火线游戏,响起熟悉的声音:headshot、double kill、multi kill、fire in the hole、......,打住!!!狂点鼠标也不会发射子弹的。好了,我们把感觉拉回来现场,继续学习Cesium。实现键盘漫游主要是通过键盘调用相机的moveForward、moveBackward、moveLeft、moveRight、moveUp、moveDown方法。下面为部分核心代码,查看完整代码请浏览GitHub地址https://github.com/ls870061011/cesium_training/tree/main/examples中的3_2部分。

    viewer.clock.onTick.addEventListener(function (clock) {
      var camera = viewer.camera;

      if (flags.looking) {
        var width = canvas.clientWidth;
        var height = canvas.clientHeight;

        // Coordinate (0.0, 0.0) will be where the mouse was clicked.
        var x = (mousePosition.x - startMousePosition.x) / width;
        var y = -(mousePosition.y - startMousePosition.y) / height;

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

      // Change movement speed based on the distance of the camera to the surface of the ellipsoid.
      var cameraHeight = ellipsoid.cartesianToCartographic(camera.position)
        .height;
      var 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: 渲染场景后立即引发的事件
    事件的添加和移除代码示例如下:
  viewer.scene.preUpdate.addEventListender(callbackFunc);
  viewer.scene.preUpdate.removeEventListender(callbackFunc);

比如我们自己写了一个指北针、标签,都可以在scene.preRender监听事件的回调函数中更新指北针状态或者是标签的位置信息。下面的部分核心代码,为场景重新选然后更新自定义标签位置。

   viewer.scene.scene-preRender.addEventListener(() => {
      if (positions instanceof Array && htmlSize instanceof Array) {
        positions.map((ele, index) => {
          const html = document.getElementById(`infoTip${index}`);
          if (html) {
            const canvasPosition = ConversionUtil.degreesToCartesian2(ele.x, ele.y, ele.z);
            if (canvasPosition) {
              html.style.top = `${canvasPosition.y - htmlSize[index].offsetHeight}px`;
              html.style.left = `${canvasPosition.x - htmlSize[index].offsetWidth}px`;
            }
          }
        });
      }
   )

你可能感兴趣的:(Cesium开发工具篇 | 02事件应用)