resium
Version:
React components for Cesium
287 lines (252 loc) • 8.16 kB
text/typescript
import {
ScreenSpaceEventType,
ScreenSpaceEventHandler,
Scene,
Cartesian2,
Primitive,
Entity,
PrimitiveCollection,
Cesium3DTileFeature,
PointPrimitiveCollection,
LabelCollection,
BillboardCollection,
PolylineCollection,
PointPrimitive,
Label,
Billboard,
Polyline,
TimeDynamicPointCloud,
ModelMesh,
ModelNode,
GroundPolylinePrimitive,
GroundPrimitive,
} from "cesium";
import { entries, includes } from "./util";
export const eventManagerContextKey = "__RESIUM_EVENT_MANAGER";
export type EventType = keyof RootEventProps;
export type RootEventTarget =
| Cesium3DTileFeature
| {
collection?:
| PrimitiveCollection
| PointPrimitiveCollection
| LabelCollection
| BillboardCollection
| PolylineCollection;
id?: Entity | string;
mesh?: ModelMesh;
node?: ModelNode;
primitive?:
| Primitive
| PointPrimitive
| Label
| Billboard
| Polyline
| TimeDynamicPointCloud
| GroundPolylinePrimitive
| GroundPrimitive;
};
export type EventProps<T> = {
onClick?: (movement: CesiumMovementEvent, target: T) => void;
onDoubleClick?: (movement: CesiumMovementEvent, target: T) => void;
onMouseDown?: (movement: CesiumMovementEvent, target: T) => void;
onMouseUp?: (movement: CesiumMovementEvent, target: T) => void;
onMiddleClick?: (movement: CesiumMovementEvent, target: T) => void;
onMiddleDown?: (movement: CesiumMovementEvent, target: T) => void;
onMiddleUp?: (movement: CesiumMovementEvent, target: T) => void;
onMouseMove?: (movement: CesiumMovementEvent, target: T) => void;
onPinchEnd?: (movement: CesiumMovementEvent, target: T) => void;
onPinchMove?: (movement: CesiumMovementEvent, target: T) => void;
onPinchStart?: (movement: CesiumMovementEvent, target: T) => void;
onRightClick?: (movement: CesiumMovementEvent, target: T) => void;
onRightDown?: (movement: CesiumMovementEvent, target: T) => void;
onRightUp?: (movement: CesiumMovementEvent, target: T) => void;
onMouseEnter?: (movement: CesiumMovementEvent, target: T) => void;
onMouseLeave?: (movement: CesiumMovementEvent, target: T) => void;
};
export type RootEventProps = EventProps<RootEventTarget> & {
onWheel?: (delta: number) => void;
};
type EventMap<T> = { [k in EventType]: T };
export type CesiumMovementEvent = {
position?: Cartesian2;
startPosition?: Cartesian2;
endPosition?: Cartesian2;
};
export type Callback<T = any> = (e: CesiumMovementEvent, source: T) => void;
export const eventNames: EventType[] = [
"onClick",
"onDoubleClick",
"onMouseDown",
"onMouseUp",
"onMiddleClick",
"onMiddleDown",
"onMiddleUp",
"onMouseMove",
"onPinchEnd",
"onPinchMove",
"onPinchStart",
"onRightClick",
"onRightDown",
"onRightUp",
"onWheel",
"onMouseEnter",
"onMouseLeave",
];
export class EventManager {
private static eventTypeMap: EventMap<ScreenSpaceEventType> = {
onClick: ScreenSpaceEventType.LEFT_CLICK,
onDoubleClick: ScreenSpaceEventType.LEFT_DOUBLE_CLICK,
onMouseDown: ScreenSpaceEventType.LEFT_DOWN,
onMouseUp: ScreenSpaceEventType.LEFT_UP,
onMiddleClick: ScreenSpaceEventType.MIDDLE_CLICK,
onMiddleDown: ScreenSpaceEventType.MIDDLE_DOWN,
onMiddleUp: ScreenSpaceEventType.MIDDLE_UP,
onMouseMove: ScreenSpaceEventType.MOUSE_MOVE,
onPinchEnd: ScreenSpaceEventType.PINCH_END,
onPinchMove: ScreenSpaceEventType.PINCH_MOVE,
onPinchStart: ScreenSpaceEventType.PINCH_START,
onRightClick: ScreenSpaceEventType.RIGHT_CLICK,
onRightDown: ScreenSpaceEventType.RIGHT_DOWN,
onRightUp: ScreenSpaceEventType.RIGHT_UP,
onWheel: ScreenSpaceEventType.WHEEL,
onMouseEnter: ScreenSpaceEventType.MOUSE_MOVE,
onMouseLeave: ScreenSpaceEventType.MOUSE_MOVE,
};
private scene: Scene | undefined;
private sshe: ScreenSpaceEventHandler;
private events: EventMap<Map<any, Callback>> = {
onClick: new Map(),
onDoubleClick: new Map(),
onMouseDown: new Map(),
onMouseUp: new Map(),
onMiddleClick: new Map(),
onMiddleDown: new Map(),
onMiddleUp: new Map(),
onMouseMove: new Map(),
onPinchEnd: new Map(),
onPinchMove: new Map(),
onPinchStart: new Map(),
onRightClick: new Map(),
onRightDown: new Map(),
onRightUp: new Map(),
onWheel: new Map(),
onMouseEnter: new Map(),
onMouseLeave: new Map(),
};
private hovered: any = undefined;
public constructor(scene?: Scene) {
this.scene = scene;
this.sshe = new ScreenSpaceEventHandler(scene?.canvas);
}
public destroy() {
this.hovered = undefined;
if (!this.sshe.isDestroyed()) {
this.sshe.destroy();
}
}
public isDestroyed() {
return this.sshe.isDestroyed();
}
public on(element: any, type: EventType, cb: Callback) {
if (element && type === "onWheel") return;
this.events[type].set(element, cb);
}
public off(element: any, type: EventType) {
this.events[type].delete(element);
if (this.hovered === element) {
this.hovered = undefined;
}
}
public setEvents(element: any, props: any) {
entries(props).forEach(([k, v]) => {
const et = k as EventType;
if (includes(eventNames, et)) {
if (v) {
this.on(element, et, v);
} else {
this.off(element, et);
}
}
});
this.commit();
}
public clearEvents(element: any) {
this.hovered = undefined;
eventNames.forEach(et => {
this.off(element, et);
});
this.commit();
}
public commit() {
const sshe = this.sshe;
const destroyed = this.sshe.isDestroyed();
if (!destroyed) {
if (
this.events.onMouseEnter.size === 0 &&
this.events.onMouseLeave.size === 0 &&
this.events.onMouseMove.size === 0
) {
this.sshe.removeInputAction(ScreenSpaceEventType.MOUSE_MOVE);
} else if (!this.sshe.getInputAction(ScreenSpaceEventType.MOUSE_MOVE)) {
this.sshe.setInputAction(this.onMouseMove, ScreenSpaceEventType.MOUSE_MOVE);
}
}
entries(this.events).forEach(([et, m]) => {
if (et === "onMouseEnter" || et === "onMouseLeave" || et === "onMouseMove") {
return;
}
const cesiumEventType = EventManager.eventTypeMap[et];
if (!destroyed) {
if (m.size === 0) {
sshe.removeInputAction(cesiumEventType);
} else if (!sshe.getInputAction(cesiumEventType)) {
sshe.setInputAction(this.eventCallback(et), cesiumEventType);
}
}
});
}
public getScreenSpaceEventHandler() {
return this.sshe;
}
private getEventCallback(type: EventType, picked: any) {
return picked === null
? this.events[type].get(null)
: this.events[type].get(picked.id) || // Entity
this.events[type].get(picked.id?.entityCollection?.owner) || // Entity in DataSource
this.events[type].get(picked.primitive) || // Primitive
this.events[type].get(picked.tileset); // Cesium3DTileFeature
}
private onMouseMove = (e: CesiumMovementEvent) => {
const picked = this.pick(e.endPosition);
if (this.hovered !== picked) {
if (this.hovered) {
this.getEventCallback("onMouseLeave", this.hovered)?.(e, this.hovered);
this.getEventCallback("onMouseLeave", null)?.(e, this.hovered);
}
if (picked) {
this.getEventCallback("onMouseEnter", picked)?.(e, picked);
this.getEventCallback("onMouseEnter", null)?.(e, picked);
}
}
if (picked) {
this.getEventCallback("onMouseMove", picked)?.(e, picked);
}
this.getEventCallback("onMouseMove", null)?.(e, picked);
this.hovered = picked;
};
private eventCallback = (et: EventType) => {
return (e: any) => {
const picked = this.pick(e?.position);
if (picked) {
this.getEventCallback(et, picked)?.(e, picked);
}
this.getEventCallback(et, null)?.(e, picked);
};
};
private pick(pos?: Cartesian2): any | undefined {
if (!pos) return undefined;
return this.scene?.pick(pos);
}
}
export default EventManager;