You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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;

代码说明

  1. 通过viewerRef获取Resium Viewer的原生Cesium实例,创建ScreenSpaceEventHandler监听左键点击事件
  2. 使用pointCallbacks的Ref存储每个原生点对应的点击回调,避免React状态更新带来的性能问题
  3. 在点击事件中,通过viewer.scene.pick()获取被点击的元素,判断是否属于目标集合后执行对应回调
  4. 组件卸载时清理事件处理器和回调映射,避免内存泄漏

内容的提问来源于stack exchange,提问作者ResiumRegent

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 19:05:09