style-only-fast-path
本文回答:
entity.styleChanged()—— 只改color/lineWidth/effect_*时的快路径,比changed()快 5~20 倍(visible/fill/lineType 不在快路径)。
一、两种 API 对比
| 方法 | 内部路径 | 代价 |
|---|---|---|
entity.changed() |
进 _changedIds → 从桶摘除 → 重跑 toGeoJSON() + CAD→经纬度变换 → 重插桶 |
重编码 + 所在桶 setData 整体重传 |
entity.styleChanged() |
进 _styleOnlyIds → renderCache.updateStyleProps(e) → 对缓存的每个 feature 调 e.updateFeatureStyleProps(feature) 原地 patch 属性 |
不重编码;所在桶照样 setData 重传 |
省的是重编码 + 坐标变换,不是 GPU 上传——桶的 GeoJSON 两种路径都要重传。实体越少动,差距越大。
二、何时必须 changed()
| 改了什么 | 必须用 changed() |
|---|---|
| 起点 / 终点 / center / radius / points | ✅ |
| rotation / transform | ✅ |
| insertionPoint / scale | ✅ |
| text 内容(文字尺寸不变情况可能 style 够用,但保守用 changed) | ✅ |
三、何时可以 styleChanged()
覆盖范围只有 3 组属性(EntityBase.updateFeatureStyleProps 唯一定义处,且无子类重写):
| 改了什么 | styleChanged() 生效? |
原因 |
|---|---|---|
color |
✅ | feature properties.color 被原地 patch |
lineWidth |
✅(文本实体除外;改回 0 除外) | patch line_width;_role 为 text_fill/text_stroke 的 feature 被跳过。只在 lineWidth > 0 时写、从不删——改回 0 后旧值残留,须 changed() |
effectColor / effectWidth / effectOpacity / effectBlur |
✅ | patch effect_* 四个属性(effectColor=-1 时改为删除) |
fillColor / fillOpacity / borderOpacity |
❌ 用 changed() |
不在 patch 清单内,缓存 feature 保留旧值 |
lineType / ltScale |
❌ 用 changed() |
虚线是** tessellate 进几何坐标**的(tessellateWithLinetype),不是样式属性 |
visible |
❌ 用 changed() |
可见性在桶重建时过滤,styleOnly 路径不重查 |
layerId |
❌ 用 changed() |
图层归属不在 patch 清单 |
minDisplayLevel / maxDisplayLevel |
❌ 用 changed() |
display-level 过滤在重建时做 |
opacity字段不存在于EntityBase(有的是fillOpacity/borderOpacity/effectOpacity)。
四、对比示例
// 慢:1 万实体每条 changed()
const t0 = performance.now();
for (const e of mapcadLayer.getEntities()) {
e.color = 0xff0000;
e.changed(); // ← 全量
}
console.log(performance.now() - t0); // 比如 2500ms
// 快:1 万实体每条 styleChanged()
const t1 = performance.now();
for (const e of mapcadLayer.getEntities()) {
e.color = 0xff0000;
e.styleChanged(); // ← 快路径
}
console.log(performance.now() - t1); // 比如 180ms
五、批量修改
for (const e of entities) {
e.color = 0xff0000;
e.styleChanged();
}
// 标脏后所在桶在下一帧合并 setData 一次(RAF 调度,无需手动 bulk)
六、bus 事件差异
bus.on(VjmapextEvents.EntityModified, ({ entityId, styleOnly }) => {
// payload 是 { entityId, styleOnly } —— 是 id 不是实体对象
if (styleOnly) {
// 来自 styleChanged()
} else {
// 来自 changed()
}
});
下游可根据 styleOnly 决定是否重建自己的索引。
七、feature-state(vjmap 底座版)
对 GeoJSON 图层,底座有类似的快路径 —— feature-state:
map.setFeatureState({ source: 'src', id: 123 }, { hover: true });
map.addLineLayer('lyr', 'src', {
lineColor: ['case',
['boolean', ['feature-state', 'hover'], false], '#f00', '#888'
]
});
不需要 setData,只改 state → 自动重绘(毫秒级)。配合表达式覆盖大范围 hover。
八、自定义实体支持快路径
没有 encode/encodeStyle/encodeGeometry 这套 API——快路径的真实挂钩是 updateFeatureStyleProps(feature):基类实现只 patch color / line_width / effect_*。自定义实体若在 toGeoJSON() 里往 properties 塞了额外的样式字段(如 fill_color、自定义角色 _role),想享受快路径就重写它:
class StarEnt extends vjmapext.EntityBase {
updateFeatureStyleProps(feature) {
super.updateFeatureStyleProps(feature); // color / line_width / effect_*
const p = feature.properties;
if (p) p.fill_color = '#' + this.fillColor.toString(16).padStart(6, '0');
}
}
不重写也能用——只是 fillColor 等自定义样式字段必须走 changed() 全量。
九、批量换色最佳实践
function setBatchColor(ents, color) {
for (const e of ents) {
if (e.color === color) continue; // 跳过无变化
e.color = color;
e.styleChanged();
}
}
- 同一帧内多次
styleChanged()合并成一次桶setData(rAF 调度) - 跳过无变化避免无谓标脏与上传
十、常见坑
- 改了几何还调 styleChanged:画面没变;用
changed()。 - 改了 fillColor / lineType / visible / layerId 用 styleChanged:同样不生效——这些不在
updateFeatureStyleProps的 patch 清单里,必须changed()。 - 自定义实体 styleChanged 不生效:自有样式字段需重写
updateFeatureStyleProps(见 §八)。 - 循环里每条 changed + styleChanged:changed 已包含 style,别再 styleChanged——而且先
changed()后styleChanged()会吞掉几何更新:styleChanged把 id 重新放回 styleOnlyIds,本帧只原地 patch 旧缓存的样式并清掉isDirty,新几何不会重算。只有反过来(先 styleChanged 后 changed,changed 会把 id 从 styleOnlyIds 摘除)才走全量。 - map 的 feature-state 不生效:source 没设
generateId: true或promoteId,feature 没 id。 - styleChanged 后事件里又 changed:变成全量;监听 styleOnly 再决定。
相关
- 渲染桶 → render-buckets
- 批量样式 → batch/bulk-style-update
- feature-state → batch/geojson-bulk