dynamic-entities
本文回答:
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 sourcesetData—— 实体越多这一下越贵
四、什么时候一定要设 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
十一、常见坑
- 忘设 dynamic:每帧都触发 cold 桶 rebuild → 明显卡顿。
- 所有实体都设 dynamic:dynamic 桶膨胀,总体 FPS 反而下降。
- dynamic 实体 changed 不触发画面刷新:确认确实调了
changed()/styleChanged();没调就只改了字段、没标脏。 - 一批实体一半 dynamic 一半 cold:两桶都要 rebuild;全 dynamic 或全 cold 更干净。
- dynamic = true 后样式变了:换桶会重新走
toGeoJSON序列化;自定义实体若toFeatureStyleProps/updateFeatureStyleProps漏写字段 → 视觉异常。
相关
- 渲染桶 → render-buckets
- 批量 → batch-rendering
- 动画性能 → event-throttling