Skip to Content
全部文章图形化Cesium 连线方案对比及选择

Cesium 连线方案对比及选择

发布时间: 2026-04-17

我是一个Cesium的初学者,最近开始在项目中开始使用Cesium开发一些基础功能,其中有个功能是连线,第一版是大模型生成的,后来我发现性能明显感觉有点不正常,渲染慢、而且每次绘制新的点network中会产生一堆重复的worker,每次绘制新的点都会产生,我意识到这里是有性能问题的,经过深入细致的查看代码,结合跟大模型学习的结论,现在使用的不断追加 polyline 到entities中对于线段数量上来之后就会有性能问题,于是我弄了个最小复现示例来最终问题。

方案

第一种entities.add

第一种的核心代码是:

function syncIssueEntityLines(viewer: Viewer, waypoints: Waypoint[]) { if (waypoints.length < 2) { return; } const index = waypoints.length - 2; const id = `${issueLinePrefix}${index}`; // 始终连线最后两个点 const positions = [toCartesian(waypoints[index]), toCartesian(waypoints[index + 1])]; viewer.entities.add({ id, polyline: { positions, width: 4, material: Color.fromCssColorString('#ff9f43'), }, }); }

它的特点是:

  1. 每次点击只处理最新形成的一段线
  2. 每一段线都是一个独立的 Entity polyline
  3. 点越多,页面里的 Entity 折线对象就越多

也就是说,第一种不是维护一条总线,而是:

每次点击就新增一个新的 Entity polyline 线段对象。

第二种 CallbackProperty

第二种的核心代码是 CallbackProperty,它是被动调用的,调用时机有些多,第二个参数设置为false表示动态模式,每帧调用一次,当调用触发时拿到最新的点坐标数组就去更新这个线实体。

function syncCallbackEntityLine(viewer: Viewer, waypointsRef: React.MutableRefObject<Waypoint[]>) { if (viewer.entities.getById(callbackLineId)) { return; } // 只创建一次polyline对象 viewer.entities.add({ id: callbackLineId, polyline: { // polyline的连线坐标是动态的,Callback会在 positions: new CallbackProperty(() => { if (waypointsRef.current.length < 2) { return []; } return waypointsRef.current.map(toCartesian); }, false), width: 4, material: Color.fromCssColorString('#f25f5c'), }, }); }

它的特点是:

  1. 整个生命周期只创建一条 Entity polyline
  2. 点击地图时不会再新增新的线对象
  3. 线的位置通过 CallbackProperty 动态读取最新点位

也就是说,第二种不是“每次点击新增一条线”,而是:

第三种 primitive

第三种的核心代码是:

function syncPrimitiveLines( polylineCollection: PolylineCollection, polylinesRef: React.MutableRefObject<Polyline[]>, waypoints: Waypoint[], ) { if (waypoints.length < 2) { return; } const index = waypoints.length - 2; const positions = [toCartesian(waypoints[index]), toCartesian(waypoints[index + 1])]; polylinesRef.current[index] = polylineCollection.add({ positions, width: 4, material: Material.fromType('Color', { color: Color.fromCssColorString('#3ddc97'), }), }); }

它的特点是:

  1. 每次点击也只处理最新形成的一段线
  2. 但新增的不是 Entity polyline
  3. 而是 PolylineCollection 里的 primitive 折线

也就是说,第三种和第一种表面上都像“每次点击新增一段线”,但本质差异是:

  1. 第一种新增的是 Entity polyline
  2. 第三种新增的是 PolylineCollection primitive

性能的差异

第一种的性能特征

第一种每次点击都会新增一个新的 Entity polyline

viewer.entities.add({ id, polyline: { positions, width: 4, material: Color.fromCssColorString('#ff9f43'), }, });

它每一次新增一条线都会重复产生一堆worker的创建: 1

它的性能特点是:

  1. 新增点位后,会新增一个新的 Entity 线对象
  2. 点越多,Entity 线对象越多
  3. Cesium 需要持续处理新增 Entity 对应的几何准备工作

所以第一种通常是这 3 种里最敏感的,也就是为什么每多一根线都会创建很多worker,肉眼可见的绘制延迟卡顿。新的 Entity polyline 被不断创建,导致 Cesium 更频繁地调度和使用 Worker 处理相关几何任务

第二种的性能特征

第二种只创建一次:

viewer.entities.add({ id: callbackLineId, polyline: { positions: new CallbackProperty(...), }, });

之后点击地图时,不再新增线对象,只是更新 waypointsRef.current,让 CallbackProperty 在渲染时读取最新点位。

只会在页面初始化时产生一堆workder调用,后期增加线渲染快

它的性能特点是:

  1. 整个页面里只有一条 Entity polyline
  2. 不会随着点击次数增加而不断创建新的 Entity 线对象
  3. 更适合“只维护一条总线”的场景

所以第二种通常会比第一种稳定很多。

第二种尤其适合这种场景:

  1. 你只关心最后是一条连续折线
  2. 点位会持续追加
  3. 你是按“点集合”管理这条线,而不是按“线段对象”管理

第二种不太适合这些场景:

  1. 只高亮某一段线
  2. 只删除某一段线
  3. 只隐藏某一段线
  4. 只给某一段线改颜色或改宽度
  5. 给每一段线绑定独立业务 id、状态、提示信息
  6. 需要把每一段线当成独立对象选中、编辑、管理

原因很简单:

第二种始终只有一条总线,没有独立的“第 1 段线对象”“第 2 段线对象”。

所以第二种更适合“按点管理整条线”,不适合“按段管理线”。

第三种的性能特征

第三种每次点击也会新增新的线段,但新增的是 primitive:

polylineCollection.add({ positions, width: 4, material: Material.fromType('Color', ...), });

同第二种,只有初始化会产生worker创建,后期不存在

它的性能特点是:

  1. 每次点击新增的是 primitive 层对象
  2. 不走 Entity polyline 那层高层封装
  3. 对高频交互绘制通常更友好

所以第三种通常适合绘制态、编辑态、交互密集型场景。

第三种更适合这种需求:

  1. 每一段线都希望作为独立对象存在
  2. 后续可能要单独操作某一段线
  3. 某一段线可能需要单独高亮、删除、修改样式
  4. 线段本身就是业务对象,而不是仅仅服务于一条整体折线

也可以把第二种和第三种的差异简单理解成:

  1. 第二种更偏“我有一组点,这些点组成一条线”
  2. 第三种更偏“我有很多段线,这些线段共同组成一条路径”

总结

少量点连线可以使用 entities.add ,但是还是不太建议,如果只要一根线,不对这些线中间某一段做特别处理选择使用 CallbackProperty ,如果要对中间的一些线做更换颜色、选中,线由比较多使用 Primitive 的方式。

最后编辑于

hi