如何为Resium PointPrimitiveCollection中的Cesium PointPrimitive添加onClick行为?
问题:动态添加的Cesium原生PointPrimitive无法触发onClick事件
我需要让添加到地图的PointPrimitive具备点击行为。发现通过Resium的<PointPrimitive>作为<PointPrimitiveCollection>子元素添加时,onClick能正常工作,但直接用Cesium原生API(PointPrimitiveCollection.add())添加的点却没有点击响应,这应该和Resium/React的机制有关。
已尝试的方案
- 使用
PointPrimitiveCollection.add添加已定义onClick方法的配置对象 - 先通过
PointPrimitiveCollection.add()添加点,再手动为其赋值onClick方法 - 用
PointPrimitiveCollection.add()添加JSX形式的<PointPrimitive>元素 - 因应用状态过大,无法通过
useState管理点列表来实现
问题根源
Resium的组件(比如<PointPrimitive>)并非直接映射Cesium原生对象的属性,而是通过React生命周期和自身事件绑定机制处理交互。原生Cesium的PointPrimitive本身没有onClick属性,Resium是在组件层面监听Cesium的screenSpaceEventHandler事件,再将点击事件映射到组件的onClick回调上。直接操作原生Cesium集合添加点时,Resium无法感知这些新元素,自然不会为它们绑定事件。
解决方案
放弃依赖Resium组件的事件绑定,直接监听Cesium Viewer的原生点击事件,手动判断点击目标并执行回调。这种方式不依赖Resium的组件逻辑,完全基于Cesium原生事件系统实现。
代码示例
import { PointPrimitive, PointPrimitiveCollection, Viewer } from 'resium'; import { Cartesian3, Color, PointPrimitiveCollection as PPC, ScreenSpaceEventType, ScreenSpaceEventHandler } from 'cesium'; import { useRef, useEffect } from 'react'; const ingestStream = () => { const collectionRef = useRef(null); const viewerRef = useRef(null); let pc: PPC | null = null; let handler: ScreenSpaceEventHandler | null = null; // 存储点与对应点击回调的映射 const pointCallbacks = useRef<Map<PPC.PointPrimitive, () => void>>(new Map()); const addPoints = (pt) => { console.log('adding') if (!pc && collectionRef.current?.cesiumElement) { pc = collectionRef.current.cesiumElement; globalThis.pointColl = pc; } if (pc) { // 添加第一个原生点 const point = pc.add({ position: Cartesian3.fromDegrees(pt.LON, pt.LAT, pt.ALT_GEOM), color: Color.YELLOW, pixelSize: 60, }); pointCallbacks.current.set(point, () => console.log('touched point')); // 添加第二个原生点 const point2 = pc.add({ position: Cartesian3.fromDegrees(pt.LON, pt.LAT + 2, pt.ALT_GEOM), color: Color.YELLOW, pixelSize: 60, }); pointCallbacks.current.set(point2, () => console.log('ow, clickage')); } }; const ptconst = { LON: -80, LAT:43, ALT_GEOM: 100 } const clickHandler = () => addPoints(ptconst) // Resium组件方式添加的点(正常响应点击) const original = <PointPrimitive position={ Cartesian3.fromDegrees(-75, 43, 100)} color={Color.CYAN} pixelSize={30} onClick={clickHandler} /> // 初始化Cesium原生点击事件监听 useEffect(() => { if (!viewerRef.current?.cesiumElement) return; const viewer = viewerRef.current.cesiumElement; handler = new ScreenSpaceEventHandler(viewer.scene.canvas); handler.setInputAction((movement) => { // 获取点击位置的拾取结果 const pick = viewer.scene.pick(movement.position); if (pick && pick.primitive && pc && pc.contains(pick.primitive)) { // 执行对应点的回调 const callback = pointCallbacks.current.get(pick.primitive as PPC.PointPrimitive); callback?.(); } }, ScreenSpaceEventType.LEFT_CLICK); // 组件卸载时清理资源 return () => { handler?.destroy(); pointCallbacks.current.clear(); }; }, []); return ( <Viewer ref={viewerRef} id="resiumContainer" full baseLayerPicker={false} geocoder={false} animation={false} timeline={false} navigationInstructionsInitiallyVisible={false} > <PointPrimitiveCollection ref={collectionRef}> {original} </PointPrimitiveCollection> </Viewer> ); }; export default ingestStream;
代码说明
- 通过
viewerRef获取Resium Viewer的原生Cesium实例,创建ScreenSpaceEventHandler监听左键点击事件 - 使用
pointCallbacks的Ref存储每个原生点对应的点击回调,避免React状态更新带来的性能问题 - 在点击事件中,通过
viewer.scene.pick()获取被点击的元素,判断是否属于目标集合后执行对应回调 - 组件卸载时清理事件处理器和回调映射,避免内存泄漏
内容的提问来源于stack exchange,提问作者ResiumRegent
相关产品推荐
相关产品推荐

