cesium.js入门基础教程四(事件)
事件简介
Cesium中大的事件按照类型进行分类,可以分为如下几种:
- 鼠标键盘事件
- 相机事件
- 数据加载事件
- 场景加载事件
按照事件的使用方式进行分类,可以分为如下两种:
- 创建事件处理器
Handler
并指定事件触发类型定义事件,如与鼠标键盘事件相关的屏幕空间事件处理器ScreenSpaceEventHandler
- Cesium中已经定义好事件回调,只需要添加事件监听的回调函数即可。如监听
3D Tiles
的瓦片全部加载完毕的回调函数allTilesLoaded
鼠标键盘事件
要定义鼠标键盘事件,首先要创建一个屏幕空间处理器ScreenSpaceEventHandler
,该方法需要传入一个HTMLCanvasElement
用于指定事件处理器应用在哪个Canvas元素上,一般情况下指定为viewer.scene.canvas
,创建一个事件处理器的完整代码如下:
const handler = new Cesium.ScreenSpaceEventHandler(viewer,scene.canvas);
这样就成功创建了一个事件处理器,接下来就要使用事件处理器上的setInputAction
方法来定义事件触发的类型和执行事件触发的回调函数。
其中鼠标的事件类型叫做 “屏幕空间事件类型ScreenSpaceEventType
”,键盘的事件类型叫做 “键盘事件修饰符KeyboardEventModifier
”
屏幕空间事件类型 ScreenSpaceEventType
因为Cesium的操作分为鼠标操作和触屏操作两种模式,所以屏幕空间事件类型ScreenSpaceEventType
分为两类:
1.鼠标操作
事件名称 | 类型 | 描述 |
---|---|---|
LEFT_DOWN |
Number |
鼠标左键按下 |
LEFT_UP |
Number |
鼠标左键弹起 |
LEFT_CLICK |
Number |
鼠标左键单击 |
LEFT_DOUBLE_CLICK |
Number |
鼠标左键双击 |
RIGHT_DOWN |
Number |
鼠标右键按下 |
RIGHT_UP |
Number |
鼠标右键弹起 |
RIGHT_CLICK |
Number |
鼠标右键单击 |
MIDDLE_DOWN |
Number |
鼠标中键按下 |
MIDDLE_UP |
Number |
鼠标中键弹起 |
MIDDLE_CLICK |
Number |
鼠标中键单击 |
MIDDLE_MOVE |
Number |
鼠标移动 |
WHEEL |
Number |
鼠标滚轮滚动 |
2.触摸屏操作
事件名称 | 类型 | 描述 |
---|---|---|
PINCH_START |
Number |
触摸屏开始两指触摸 |
PINCH_END |
Number |
触摸屏结束两指触摸 |
PINCH_MOVE |
Number |
触摸屏两指触摸移动 |
键盘事件修饰符KeyboardEventModifier
事件名称 | 类型 | 描述 |
---|---|---|
SHIFT |
Number |
shift 键被按住 |
CTRL |
Number |
ctrl 键被按住 |
ALT |
Number |
ALT 键被按住 |
事件注册方法 setInputAction
事件注册方法setInputAction
用于根据事件类型定义事件的触发方式并执行对应的回调函数,该方法的描述如下:
属性名称 | 类型 | 描述 |
---|---|---|
action |
指定不同的type 值会触发不同的回调函数,详见官网 |
shift 键被按住 |
CTRL |
屏幕空间事件类型ScreenSpaceEventType |
屏幕空间事件类型 |
ALT |
键盘事件修饰符KeyboardEventModifier |
可选项,键盘事件修饰符 |
前面提到想要定义一个事件,首先要创建一个事件处理器handler
,再使用事件处理器handler
上的setInputAction
方法来指定事件的类型和事件的回调,如下面代码:
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene,canvas);
handler.setInputAction(movement=>{
//do someting
},Cesium.ScreenSpaceEventType.LEFT_CLICK)
至于在回调函数中做些什么,是由触发的回调函数来决定的
在方法描述中可以看到键盘事件修饰符modifier
为可选项,在Cesium中键盘事件类型是无法单独使用的,需要和鼠标事件类型一起使用,如下代码表示按住键盘CTRL
键并且点击鼠标左键才能触发事件回调函数:
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene,canvas);
handler.setInputAction(movement=>{
//do someting
},Cesium.ScreenSpaceEventType.LEFT_CLICK,Cesium.KeyboardEventModifier.CTRL)
事件回调函数
由于触发事件的类型各异,所以对应的也有不同的事件回调来处理不同的事件,事件回调函数分为如下几种:
PositionedEventCallback(event)
:所触发的事件类型为所有鼠标点击事件LEFT_DOWN
、LEFT_UP
、LEFT_CLICK
、LEFT_DOUBLE_CLICK
、RIGHT_DOWN
、RIGHT_UP
、RIGHT_CLICK
、MIDDLE_DOWN
、MIDDLE_UP
、MIDDLE_CLICK
;MotionEventCallback(event)
:所触发的事件类型为鼠标移动事件:MOUSE_MOVE
WheelEventCallback(delte)
: 所触发的事件类型为鼠标滚轮滚动事件:WHEEL
;TwoPointEventCallback(evnet)
:所触发的事件类型为触摸屏两指触摸事件:PINCH_SRART
、PINCH_END
TwoPointMotionEventCallback(event)
:所触发事件类型为触摸屏两指移动事件:PINCH_MOVE
PositionedEventCallback(event)
该回调函数会返回一个Cartesian2
对象:
参数名称 | 类型 | 描述 |
---|---|---|
position |
Cartesian2 |
鼠标点击的笛卡尔平面直角坐标系 |
代码如下:
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(position=>{
console.log(position)
},Cesium.ScreenSpaceEventType.LEFT_CLICK)
WheelEventCallback(delta)
该回调函数会返回一个delta
值,为鼠标滚轮滚动的值:
参数名称 | 类型 | 描述 |
---|---|---|
delta |
Number |
鼠标滚轮滚动的值 |
代码如下:
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(delta=>{
//do something
},Cesium.ScreenSpaceEventType.WHEEL)
移除事件 removeInputAction
定义Cesium中的事件往往是执行某一个具体的操作,如鼠标点击拾取,当操作执行结束后需要移除该事件,避免对后续操作造成影像,移除事件的方法removeInputAction,该方法的描述如下:
参数名称 | 类型 | 描述 |
---|---|---|
type |
ScreenSpaceEventType |
屏幕空间事件类型 |
type |
KeyboardEventModifier |
可选项,键盘事件修饰符 |
使用方法如下:
handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
相机事件
与相机相关的事件又三个:
viewer.cameara.changed
:当相机改变触发viewer.camera.moveStart
:当相机开始移动时触发viewer.camera.moveEnd
:当相机结束移动时触发
由于这三个事件都为Cesium.Event()
类型,因此需要使用addEventListener
方法田间事件监听并指定回调函数,如下代码:
viewer.camera.changed.addEventListener(()=>{
//camera changed
});
viewer.camera.moveStart.addEventListener(()=>{
//camera moveStart
});
viewer.camera.moveEnd.addEventListener(()=>{
//camera moveEnd
});
数据加载事件
在Cesium中,常见的数据加载类型和相关事件包括以下几种:
Cesium3DTileset
常见事件如下:
allTilesLoaded
:当所有满足屏幕空间误差的瓦片集加载完成后触发,此时瓦片集已经完全加载;initialTilesLoaded
:当所有满足屏幕空间误差的瓦片集加载完成后触发,此时初始视图已经加载完成,该方法的触发在allTilesLoaded
之后;loadProgress
:当请求瓦片时触发,该方法会提供请求挂起瓦片的数量numberOfPendingRequests
和正在处理的瓦片数量numberOfTilesProcessing
两个参数tileFailed
:当瓦片请求失败时触发,该方法会提供一个error
对象,包含请求失败的瓦片url和请求失败的原因message
tileUnload
:当一个瓦片卸载后触发,卸载指的是从内存中删除,被卸载的原因因为设置maximumMemoryUsage
大小不足,或是trimLoadedTiles()
方法被调用tileLoad
:当一个瓦片加载后触发tileVisible
:当一个瓦片可见时触发
Entity
常见事件如下:
definitionChanged
:当实体的属性发生改变时触发
DataSource
常见事件如下:
changedEvent
:当数据发生改变时触发errorEvent
:当数据加载时发生错误时触发loadingEvent
:当数据的值发生改变时触发
Cesium3DTileset加载示例
本例中将使用allTilesLoaded
、initialTilesLoaded
、loadProgress
和tileLoad
这四个事件说明3D tiles
的加载过程。
// 加载3D tiles
const tileset = viewer.scene.primitives.add(
new Cesium.Cesium3DTileset({
url: '../3dtiles/tilesset/tileset.json',
})
)
// 视角定位到3D tiles
viewer.zoomTo(tileset)
// allTilesLoaded 瓦片集加载完成
tileset.allTilesLoaded.addEventListener(() => {
console.log('allTilesLoaded,瓦片集加载完成')
})
// initialTilesLoaded 瓦片集加载完成,并且场景初始化完成
tileset.initialTilesLoaded.addEventListener(() => {
console.log('initialTilesLoaded,瓦片集加载完成,并且场景初始化完成')
})
// loadProgress 瓦片加载进度
tileset.loadProgress.addEventListener((numberOfPendingRequests, numberOfTilesProcessing) => {
if ((numberOfPendingRequests === 0) && (numberOfTilesProcessing === 0)) {
console.log('loadProgress,加载完成')
return
}
console.log('loadProgress,请求挂起的瓦片数量' + numberOfPendingRequests + ', 正在处理的瓦片数量: ' + numberOfTilesProcessing);
})
// tileLoad 一个瓦片被加载
tileset.tileLoad.addEventListener(tile => {
console.log('tileLoad,一个瓦片被加载了', tile)
})
// 显示3D tiles的瓦片包围盒
tileset.debugShowContentBoundingVolume = true
根据控制台输出可以得到如下结论:
- 最先被调用的是loadProgress,表示当前正处于瓦片请求阶段;
- 第二个被调用的是tileLoad,表示当前正处于单个瓦片加载阶段,根据3D tiles瓦片包围盒可以看到共有5个Cesium3Dtiles,因此该事件被触发了5次;
- 第三个被调用的是allTilesLoaded,表示满足当前屏幕空间误差的瓦片集已经完全加载;
- 最后一个被调用的是initialTilesLoaded,表示满足当前屏幕空间误差的瓦片集已经完全加载,并且初始化视图已经完成。
场景渲染事件
在Cesium中,与场景渲染也有关的事件都在Scene对象中,常见的事件如下:
postRender
:当场景渲染完成后触发postUpdate
:在场景更新后渲染场景之前立即触发的事件preRender
:在场景渲染之前触发preUpdate
:在更新或渲染场景之前触发的事件terrainProviderChanged
:当地形提供者发生改变时触发
场景渲染示例
本实例中将使用postRender
、postUpdate
、preRender
和preUpdate
这四个事件说明场景Scene
的渲染过程
主要代码如下:
viewer.scene.postRender.addEventListener(() => {
console.log('postRender')
})
viewer.scene.postUpdate.addEventListener(() => {
console.log('postUpdate')
})
viewer.scene.preRender.addEventListener(() => {
console.log('preRender')
})
viewer.scene.preUpdate.addEventListener(() => {
console.log('preUpdate')
})
由于Cesium会自动开启渲染循环RenderLoop
,所以在使用该示例时,要关闭Cesium的自动渲染,关闭方法如下:
viewer.useDefaultRenderLoop = false // 关闭Cesium场景自动渲染
关闭后需要主动触发渲染,触发的方法如下:
viewer.render() // 主动触发Ceisum场景渲染
- 最先被调用的是preUpdate,表示当前正处于场景更新之前阶段;
- 第二个被调用的是postUpdate,表示当前正处于场景更新之后阶段;
- 第三个被调用的是preRender,表示当前正处于场景渲染之前阶段;
- 最后一个被调用的是postRender,表示当前正处于场景渲染之后阶段。
总的来说,更新Update操作是先于渲染Render操作的,可以使用这些事件将一些外部DOM操作放在事件内,避免渲染后操作造成页面抖动。