CesiumJS中Selection Reticle的定义及类似功能实现问询
关于Cesium Sandcastle中点击实体显示的绿色十字线(Reticle)
首先要明确:这个绿色方形十字线并不是Cesium核心库的内置组件,而是Sandcastle示例框架自己实现的自定义交互功能,所以你在Cesium官方文档和核心代码库里找不到直接对应的定义。
下面我会给你拆解实现思路,以及一套可以直接复用的类似功能代码:
实现原理
核心逻辑就是3步:
- 监听鼠标左键点击事件,判断是否点击到了实体
- 如果命中实体,将实体的3D位置转换为屏幕坐标,在对应位置渲染十字线
- 实时更新十字线位置(如果实体移动),或者在点击空白处/实体移出视野时隐藏十字线
具体实现方案
这里提供两种常见的实现方式,你可以根据需求选择:
方案1:用HTML元素实现屏幕空间十字线(简单灵活)
这种方式不需要操作Cesium的3D图元,直接用普通HTML元素做十字线,性能开销小,样式易定制。
- 先在HTML中添加十字线元素:
<div id="entity-reticle" style=" position: absolute; width: 24px; height: 24px; border: 2px solid #00ff00; border-radius: 2px; pointer-events: none; /* 避免遮挡Cesium的交互 */ display: none; z-index: 1000; "></div>
- 然后在JavaScript中添加交互逻辑:
const viewer = new Cesium.Viewer('cesiumContainer', { selectionIndicator: false // 你已经禁用的选择部件 }); const reticleElement = document.getElementById('entity-reticle'); let selectedEntity = null; // 监听左键点击事件 viewer.screenSpaceEventHandler.setInputAction((clickEvent) => { const pickedResult = viewer.scene.pick(clickEvent.position); // 判断是否点击到了实体 if (Cesium.defined(pickedResult) && Cesium.defined(pickedResult.id)) { selectedEntity = pickedResult.id; updateReticlePosition(); reticleElement.style.display = 'block'; } else { // 未点击到实体,隐藏十字线 selectedEntity = null; reticleElement.style.display = 'none'; } }, Cesium.ScreenSpaceEventType.LEFT_CLICK); // 实时更新十字线位置(实体移动或视角变化时) viewer.scene.preRender.addEventListener(() => { if (Cesium.defined(selectedEntity)) { updateReticlePosition(); } }); // 封装更新十字线位置的函数 function updateReticlePosition() { // 获取实体的当前3D位置 const entityPosition = selectedEntity.position.getValue(viewer.clock.currentTime); if (!Cesium.defined(entityPosition)) { reticleElement.style.display = 'none'; return; } // 转换为屏幕坐标 const screenPosition = viewer.scene.cartesianToCanvasCoordinates(entityPosition); if (Cesium.defined(screenPosition)) { // 让十字线居中对齐点击位置 reticleElement.style.left = `${screenPosition.x - 12}px`; reticleElement.style.top = `${screenPosition.y - 12}px`; } else { // 实体不在当前视野内,隐藏十字线 reticleElement.style.display = 'none'; } }
方案2:用Cesium Primitive实现3D空间十字线(更贴合3D场景)
如果需要十字线跟随3D场景的深度(比如被遮挡时隐藏),可以用Cesium的Primitive来创建3D空间的十字线:
const viewer = new Cesium.Viewer('cesiumContainer', { selectionIndicator: false }); let reticlePrimitive = null; let selectedEntity = null; // 创建十字线的Primitive function create3DReticle() { const reticleGeometry = new Cesium.GeometryInstance({ geometry: new Cesium.RectangleGeometry({ rectangle: Cesium.Rectangle.fromDegrees(-0.00015, -0.00015, 0.00015, 0.00015), vertexFormat: Cesium.VertexFormat.POSITION_ONLY }), attributes: { color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.GREEN.withAlpha(0.8)) } }); return viewer.scene.primitives.add(new Cesium.Primitive({ geometryInstances: reticleGeometry, appearance: new Cesium.PerInstanceColorAppearance({ flat: true, translucent: false }), show: false })); } reticlePrimitive = create3DReticle(); // 监听点击事件 viewer.screenSpaceEventHandler.setInputAction((clickEvent) => { const pickedResult = viewer.scene.pick(clickEvent.position); if (Cesium.defined(pickedResult) && Cesium.defined(pickedResult.id)) { selectedEntity = pickedResult.id; update3DReticlePosition(); reticlePrimitive.show = true; } else { selectedEntity = null; reticlePrimitive.show = false; } }, Cesium.ScreenSpaceEventType.LEFT_CLICK); // 实时更新3D十字线位置 viewer.clock.onTick.addEventListener(() => { if (Cesium.defined(selectedEntity)) { update3DReticlePosition(); } }); function update3DReticlePosition() { const entityPosition = selectedEntity.position.getValue(viewer.clock.currentTime); if (!Cesium.defined(entityPosition)) { reticlePrimitive.show = false; return; } // 获取实体的经纬度 const cartographic = Cesium.Cartographic.fromCartesian(entityPosition); // 更新十字线的位置 reticlePrimitive.geometryInstances = new Cesium.GeometryInstance({ geometry: new Cesium.RectangleGeometry({ rectangle: Cesium.Rectangle.fromDegrees( cartographic.longitude - 0.00015, cartographic.latitude - 0.00015, cartographic.longitude + 0.00015, cartographic.latitude + 0.00015 ), vertexFormat: Cesium.VertexFormat.POSITION_ONLY }), attributes: { color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.GREEN.withAlpha(0.8)) } }); }
补充说明
Sandcastle里的实现逻辑和上面的方案类似,只是封装得更简洁,并且集成到了Sandcastle的示例框架中。因为它不属于Cesium核心功能,所以官方文档没有单独的说明条目。
内容的提问来源于stack exchange,提问作者Zac
相关产品推荐
相关产品推荐

