无论是前端系统,还是二维/三维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); } });
场景渲染事件主要包括以下四种:
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`; } } }); } )
微信扫描下方二维码,获取更多资讯。