本学习系列将以Cesium + Vue3 + Vite +Typescript+elementplus作为主要技术栈展开,后续会循序渐进,持续探索Cesium的高级功能,相关源码全部免费公开。 请关注文中图片右下角gzh,关注后私信“Cesium学习系列源码”,即可看到全部代码。
上篇文章里说到,在Cesium里一个 ScreenSpaceEventHandler 上同一个 ScreenSpaceEventType 只能同时挂一个回调。实时获取鼠标三维坐标数据会与上标绘、测量等功能的MouseMove事件可能会有冲突,本篇采用事件派发器统一管理Cesium事件,实时显示鼠标三维坐标数据、实时显示相机姿态数据。
1、编写EventDispatcher类
import { DrawEventType } from "../Common/enums";
/**
* 事件总线管理,事件派发器:负责管理画笔/编辑相关事件的订阅、撤销与派发。
*/
export default class EventDispatcher {
/** 监听器池,按事件类型存储多个回调函数 */
listeners: Map<DrawEventType, Set<EventListener>>;
constructor() {
// 初始化时,预置所有支持的事件类型,避免运行时动态添加
this.listeners = new Map([
[ DRAWSTART , new Set()],
[ DRAWUPDATE , new Set()],
[ DRAWEND , new Set()],
[ EDITSTART , new Set()],
[ EDITEND , new Set()],
[ MOUSEMOVE , new Set()],
[ WHEEL , new Set()],
]);
}
/**
* 注册事件监听器
* @param event 事件类型,必须是预置的 DrawEventType 之一
* @param listener 回调函数,事件触发时会收到 eventData
*/
on(event: DrawEventType, listener: EventListener) {
if (!this.listeners.has(event)) {
console.warn("事件类型必须是 DRAWSTART , DRAWUPDATE , DRAWEND , EDITSTART , EDITEND , MOUSEMOVE , WHEEL 之一");
return;
}
this.listeners.get(event)?.add(listener);
}
/**
* 移除事件监听器
* @param event 事件类型
* @param listener 之前注册过的回调函数引用
*/
off(event: DrawEventType, listener: EventListener) {
if (this.listeners.has(event)) {
this.listeners.get(event)?.delete(listener);
}
}
/**
* 派发(触发)指定事件
* @param event 事件类型
* @param eventData 随事件携带的任意数据,会透传给所有监听器
*/
dispatchEvent(event: DrawEventType, eventData?: any) {
if (this.listeners.has(event)) {
this.listeners.get(event)?.forEach((listener) => {
listener(eventData);
});
}
}
/** 一次性监听 */
once(event: DrawEventType, listener: EventListener) {
const wrap: EventListener = (data) => {
this.off(event, wrap);
listener(data);
};
this.on(event, wrap);
}
/** 清空某类事件 */
clear(event: DrawEventType) {
this.listeners.get(event)?.clear();
}
}
2、新建MouseInfoPickerInViewer,用于绑定事件派发的鼠标动作,并拿到回调的数据。
import EventDispatcher from "@/system/EventDispatcher/EventDispatcher";
import { Cartographic, ScreenSpaceEventHandler, ScreenSpaceEventType, Viewer } from "cesium";
export default class MouseInfoPickerInViewer {
private handler: ScreenSpaceEventHandler;
private dispatcher = new EventDispatcher();
private viewer: Viewer;
constructor(viewer: Viewer) {
this.viewer = viewer;
this.handler = new ScreenSpaceEventHandler(viewer.scene.canvas);
this.bindMouseMove();
this.bindWheel();
}
/** 外部订阅:鼠标移动 */
onMouseMove(cb: (coords: Cartographic) => void) {
// Wrap the callback to match EventListener signature
const wrapper = (evt: any) => cb(evt as Cartographic);
this.dispatcher.on( MOUSEMOVE , wrapper);
// Return a function to remove the wrapper
return () => this.dispatcher.off( MOUSEMOVE , wrapper); // 返回撤销函数
}
onMouseWheel(cb: (h: number) => void) {
// Wrap the callback to match EventListener signature
const wrapper = (evt: any) => cb(evt as number);
this.dispatcher.on( WHEEL , wrapper);
// Return a function to remove the wrapper
return () => this.dispatcher.off( WHEEL , wrapper); // 返回撤销函数
}
private bindMouseMove() {
this.handler.setInputAction((movement: any) => {
// 获取鼠标在屏幕上的位置
const screenPosition = movement.endPosition
// 将屏幕位置转换为经纬度
const cartesian = this.viewer.scene.globe.pick(this.viewer.camera.getPickRay(screenPosition)!, this.viewer.scene)
// const cartesian = viewer.camera.pickEllipsoid(movement.endPosition,CesiumViewer.ellipsoid)
if (cartesian) {
const cartographic = this.viewer.scene.globe.ellipsoid.cartesianToCartographic(cartesian)
this.dispatcher.dispatchEvent( MOUSEMOVE , cartographic)
}
}, ScreenSpaceEventType.MOUSE_MOVE);
}
private bindWheel() {
this.handler.setInputAction(() => {
this.dispatcher.dispatchEvent( WHEEL , this.viewer.camera.positionCartographic.height)
}, ScreenSpaceEventType.WHEEL);
}
destroy() {
this.handler.destroy()
this.dispatcher.clear( MOUSEMOVE )
this.dispatcher.clear( WHEEL )
}
}
3、然后调用MouseInfoPickerInViewer里的onMouseMove和onMouseWheel即可
const picker = new MouseInfoPickerInViewer(viewer);
picker.onMouseMove((cartographic) => {
//鼠标位置
MouseStatusInViewer.longtitude.value = Cesium.Math.toDegrees(cartographic?.longitude ?? 0)
MouseStatusInViewer.latitude.value = Cesium.Math.toDegrees(cartographic?.latitude ?? 0)
MouseStatusInViewer.altitude.value = viewer.scene.globe.getHeight(cartographic) ?? 0
//相机信息
MouseStatusInViewer.heading.value = Cesium.Math.toDegrees(viewer.camera.heading ?? 0)
MouseStatusInViewer.pitch.value = Cesium.Math.toDegrees(viewer.camera.pitch ?? 0)
MouseStatusInViewer.roll.value = Cesium.Math.toDegrees(viewer.camera.roll ?? 0)
});
picker.onMouseWheel((h) => {
MouseStatusInViewer.cameraHeight.value = h
})
4、实现效果

¥348.48
树莓派4代3b+ SIM7600CE 4G扩展板 GNSS定位 支持4G/3G/2G通信
¥7.20
适用于华为手表3 Pro钢化膜华为儿童电话手表3贴膜ELF-G00/SIM-AL00保护膜华为儿童手表4X钢化膜4X护眼膜屏幕
¥1817.89
CELIA-U-GELS3【USB TO CELL MOD NO SIM VERIZON】
¥32.00
CY辰阳 MINI PCIE转USB 3G 4G模块 测试开发板NGFF 含SIM UIM卡座
¥493.00
【预订】Simulationstechnik: 3. Symposium Sim...
¥1641.46
DAP3BC-S3-6【DATA ACCESS PORT, 3IN. BASE, SIM】