美文网首页Cesium开发基础
Cesium开发工具篇 | 02事件应用

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

作者: GIS李胜 | 来源:发表于2021-02-16 23:57 被阅读0次

    无论是前端系统,还是二维/三维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事件应用

        本文链接:https://www.haomeiwen.com/subject/ssobxltx.html