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

CesiumJS中Selection Reticle的定义及类似功能实现问询

关于Cesium Sandcastle中点击实体显示的绿色十字线(Reticle)

首先要明确:这个绿色方形十字线并不是Cesium核心库的内置组件,而是Sandcastle示例框架自己实现的自定义交互功能,所以你在Cesium官方文档和核心代码库里找不到直接对应的定义。

下面我会给你拆解实现思路,以及一套可以直接复用的类似功能代码:

实现原理

核心逻辑就是3步:

  • 监听鼠标左键点击事件,判断是否点击到了实体
  • 如果命中实体,将实体的3D位置转换为屏幕坐标,在对应位置渲染十字线
  • 实时更新十字线位置(如果实体移动),或者在点击空白处/实体移出视野时隐藏十字线

具体实现方案

这里提供两种常见的实现方式,你可以根据需求选择:

方案1:用HTML元素实现屏幕空间十字线(简单灵活)

这种方式不需要操作Cesium的3D图元,直接用普通HTML元素做十字线,性能开销小,样式易定制。

  1. 先在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>
  1. 然后在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 04:08:52