唯杰地图 logo唯杰地图
VJMAP
EN

bus-entity

vjmapext/references/event/bus-entity.md 5.8 KB167 行
原文

本文回答:mapcadLayer.eventBus 实体事件 —— entity:added / deleted / modified / click / dblclick / mouseenter / mouseleave。

一、事件列表

const { VjmapextEvents } = vjmapext;
常量 字符串 payload
EntityAdded entity:added { entity }
EntityDeleted entity:deleted { entity }
EntityModified entity:modified { entityId, styleOnly? } ⚠️ 是 entityId 不是 entity
EntityClick entity:click { entityId, entity, cadPoint, originalEvent }
EntityDblClick entity:dblclick { entityId, entity, cadPoint, originalEvent }
EntityMouseEnter entity:mouseenter { entityId, entity, originalEvent }
EntityMouseLeave entity:mouseleave { entityId, entity }

二、生命周期:增删改

const bus = mapcadLayer.eventBus;

bus.on(VjmapextEvents.EntityAdded, ({ entity }) => {
  console.log('new:', entity.id, entity.type);
});

bus.on(VjmapextEvents.EntityDeleted, ({ entity }) => {
  console.log('del:', entity.id);
});

bus.on(VjmapextEvents.EntityModified, ({ entityId, styleOnly }) => {
  const entity = mapcadLayer.store.get(entityId);
  if (styleOnly) {
    // 只动了颜色 / 线宽 / 可见;不必重算空间索引
  } else {
    // 几何变化;重算你自己的索引
  }
});

styleOnly 标志

entity.styleChanged() 触发 modified 且 styleOnly=true;entity.changed() 触发 modified 且不带 styleOnly 字段(即 undefined,不是 false)。

三、实体鼠标事件

bus.on(VjmapextEvents.EntityClick, ({ entity, originalEvent }) => {
  console.log('click:', entity.id, entity.type);
});

bus.on(VjmapextEvents.EntityDblClick, ({ entity }) => {
  if (entity.type === 'TEXT' || entity.type === 'MTEXT') {
    mapcadLayer.executeCommand('TEXTEDIT');
  }
});

bus.on(VjmapextEvents.EntityMouseEnter, ({ entity }) => {
  entity.setCustomProperty('__prevColor', entity.color);
  entity.color = 0xff0000;
  entity.styleChanged();
});

bus.on(VjmapextEvents.EntityMouseLeave, ({ entity }) => {
  entity.color = entity.getCustomProperty('__prevColor')?.value ?? entity.color;  // 返回 {type,value}
  entity.styleChanged();
});

四、结合修饰键

entity:click 在内置选中逻辑之前同步发出,handler 返回后内置逻辑会再执行(普通单击 setSelection([id]) 替换选集、Shift+单击 toggle)——handler 里直接 setSelection 会被立刻覆盖。内置的多选键是 Shift;非要 Ctrl 多选,得等内置逻辑跑完再改:

bus.on(VjmapextEvents.EntityClick, ({ entity, originalEvent }) => {
  if (!originalEvent.ctrlKey || originalEvent.shiftKey) return;   // 其余交给内置逻辑
  const ids = mapcadLayer.store.getSelectedIds();                  // 内置逻辑执行前的选集(副本)
  const i = ids.indexOf(entity.id);
  if (i >= 0) ids.splice(i, 1);
  else ids.push(entity.id);
  queueMicrotask(() => mapcadLayer.store.setSelection(ids));      // 在内置 setSelection([id]) 之后再覆盖
});

五、实体批量统计

let count = 0;
bus.on(VjmapextEvents.EntityAdded, () => { count++; refresh(); });
bus.on(VjmapextEvents.EntityDeleted, () => { count--; refresh(); });

六、性能提示

  • 上万 entity 同时 add 会触发上万次 entity:added → handler 里不要做昂贵操作,或合并到 command:ended 统一处理(store:changed 在 1.0.1 从未发射,不能用作聚合出口,见 bus-render-store)。
  • entity:mouseenter / leave 只在悬停对象切换时 fire(同一实体上移动不重复发),且只在空闲态(非命令输入/拖拽中)检测;悬停到夹点上也会先发 leave。handler 本身不必节流,但别做重活。

七、事件级联防死循环

bus.on(VjmapextEvents.EntityModified, ({ entityId }) => {
  const entity = mapcadLayer.store.get(entityId);
  // 错误示例:这里再次改 entity → 又触发 modified → 无限
  entity.color = 0xff0000;
  entity.styleChanged();
});

正确做法:

const processed = new WeakSet();
bus.on(VjmapextEvents.EntityModified, ({ entityId, styleOnly }) => {
  if (styleOnly) return;        // 样式变化不再二次改
  const entity = mapcadLayer.store.get(entityId);
  if (processed.has(entity)) return;
  processed.add(entity);
  entity.color = 0xff0000;
  entity.styleChanged();
});

八、在命令期间捕获新增

let capturing = false;
const captured = [];

bus.on(VjmapextEvents.CommandStarted, ({ name }) => {
  if (name === 'LINE') { capturing = true; captured.length = 0; }
});
bus.on(VjmapextEvents.CommandEnded, ({ name }) => {
  if (name === 'LINE') {
    capturing = false;
    console.log('LINE 命令新增了', captured.length, '条');
  }
});
bus.on(VjmapextEvents.EntityAdded, ({ entity }) => {
  if (capturing) captured.push(entity);
});

九、off / once

const handler = ({ entity }) => { ... };
bus.on(VjmapextEvents.EntityAdded, handler);
bus.off(VjmapextEvents.EntityAdded, handler);

bus.once(VjmapextEvents.EntityAdded, handler);   // 只触发一次

十、常见坑

  1. mouseenter 每次进离重复绑样式:用 _prevColor 缓存,避免连续 enter 导致绿色反转。
  2. entity:added 在 store.add 之后:不要在 handler 里再 store.add 同一个实体。
  3. 命令中 fire entity:click? 命令执行期间默认 fire 的是 input 事件,实体 click 可能被屏蔽。
  4. selection 和 click 顺序:实体 click → 自动 selection:changed;监听任一都可以得到选中。
  5. 匿名函数无 off → 清理困难。

相关