唯杰地图 logo唯杰地图
VJMAP
EN

dynamic-entities

vjmapext/references/perf/dynamic-entities.md 5.2 KB141 行
原文

本文回答:entity.dynamic = true —— 为什么需要、何时用、性能差别。

一、问题:静态实体频繁改,画面不更新 / 非常卡

const line = new vjmapext.LineEnt(p1, p2);
mapcadLayer.addEntity(line);

setInterval(() => {
  line.endPoint.x += 1;   // 原地改字段,保持 Point2D 实例(别赋 {x,y} 字面量,getGripPoints/clone 路径会崩)
  line.changed();
}, 16);

现象:

  • 画面每帧都更新(正确)
  • 但每帧都会对 cold 桶整条 GeoJSON source setData——上万实体的 features 重新序列化上传一次
  • 导致 10 FPS

二、解法:entity.dynamic = true

const line = new vjmapext.LineEnt(p1, p2);
line.dynamic = true;           // ← 关键
mapcadLayer.addEntity(line);

此后 line.changed() 只会让 dyn 桶那条 GeoJSON source 重 setData,不再牵连几万条的 cold source。

三、语义

dynamic = true 告诉渲染管线把该实体的 features 放进独立的 dyn 桶。渲染管线内部是三条 GeoJSON source(hot/cold/dyn,不是 GL buffer):

  • 实体进 dyn 桶(独立 FeatureCollection source)
  • changed() → 增量更新只重算该实体的 features,再对 dyn source 调 setData(几十/几百条),cold source(几万条)不动
  • styleChanged() 走 styleOnly 快路径:直接回写缓存 feature 属性,连重算都省(见 style-only-fast-path)
  • 没设 dynamic 时,changed() 会让整条 cold source setData —— 实体越多这一下越贵

四、什么时候一定要设 dynamic

场景 必须 dynamic
实体每帧改位置(路径跟随、平移动画) ✅
实体每帧改颜色 / alpha(闪烁、脉冲) ✅
用 动画/特效系统 驱动 ✅
跟随鼠标 / 橡皮筋 ✅(或走 PreviewManager)

五、什么时候不要设 dynamic

场景 保持 cold
从 DWG 加载几万静态实体 ❌(全用 dynamic 反而慢)
用户偶尔点一下改颜色 ❌(hot 桶会自动处理)
一次画完不再动 ❌

规则:默认不动;只给真正高频变化的实体单独开。

六、dynamic + 动画

const circle = new vjmapext.CircleEnt([0, 0], 10);
circle.dynamic = true;
mapcadLayer.addEntity(circle);

let t = 0;
function tick() {
  t += 0.016;
  circle.radius = 10 + 5 * Math.sin(t);
  circle.color = 0xff0000 + Math.floor(127 * Math.sin(t));
  circle.changed();
  requestAnimationFrame(tick);
}
tick();

不设 dynamic 的话,每帧都会让 cold 桶一起重建,整张图都卡。

七、dynamic 实体怎么迁回 cold

场景:动画结束后想变回"静态"。

circle.dynamic = false;
circle.changed();           // 触发重分桶

内部:实体从 dynamic 桶移出,进 cold 桶(下一次批次 rebuild 时合并)。

但一般不用管:dynamic 桶空间充足,留着也没事。

八、dynamic + bulk

for (const e of loadedEntities) {
  e.dynamic = true;
  mapcadLayer.addEntity(e);
}

允许但不推荐:dynamic 桶一次塞 1 万条也会慢。对于一次性加载的大量数据,走 cold 实体更快(重绘由 RAF 自动合并,不需要手动 bulk)。

九、查看 dynamic 实体数

const dynCount = mapcadLayer.getEntities().filter(e => e.dynamic).length;
console.log('dynamic 实体:', dynCount);

正常业务 dynamic < 100;超过 1000 说明过度使用。

十、和动画系统的关系

vjmapext 的 AnimationManager(顶层导出,另有 KeyframeAnimator / PathAnimator / SpringAnimator,四类均可 new)只是一个 rAF 回调调度器:它不会替你设 dynamic,也不替你标脏——不管用它还是手写 setInterval,高频改动的实体都要自己 ent.dynamic = true,并在回调里 changed()/styleChanged()。

const am = mapcadLayer.animationManager;
circle.dynamic = true;                       // 自己设
am.add('blink', (dt, now) => {             // dt=距上次回调毫秒数,now=rAF 时间戳(不是累计时长)
  circle.radius = 50 + 10 * Math.sin(now / 300);
  circle.changed();
}, { maxFps: 60 });                          // 默认每个回调限 30fps;maxFps:0 = 每帧
am.remove('blink');
// 另有 am.addTween(id, tweenOptions, { maxFps }) 与
// am.addPathAnimation(id, pathOptions, (pos, angle, t) => {...}, { maxFps }) → 返回 PathAnimator

十一、常见坑

  1. 忘设 dynamic:每帧都触发 cold 桶 rebuild → 明显卡顿。
  2. 所有实体都设 dynamic:dynamic 桶膨胀,总体 FPS 反而下降。
  3. dynamic 实体 changed 不触发画面刷新:确认确实调了 changed()/styleChanged();没调就只改了字段、没标脏。
  4. 一批实体一半 dynamic 一半 cold:两桶都要 rebuild;全 dynamic 或全 cold 更干净。
  5. dynamic = true 后样式变了:换桶会重新走 toGeoJSON 序列化;自定义实体若 toFeatureStyleProps/updateFeatureStyleProps 漏写字段 → 视觉异常。

相关