唯杰地图 logo唯杰地图
VJMAP
EN

bus-selection

vjmapext/references/event/bus-selection.md 5.3 KB146 行
原文

本文回答:选集事件 —— selection:changed / boxselect:started / boxselect:ended。

一、事件列表

常量 字符串 payload
SelectionChanged selection:changed { selectedIds: string[] }
BoxSelectStarted boxselect:started { mode }——恒为 "window"(开始时不分左右拖向)
BoxSelectEnded boxselect:ended { mode, count, entityIds }——mode 这里才是终态:"window"(左→右)或 "crossing"(右→左)

二、selection:changed

全量快照 payload(运行时核实:payload 是 { selectedIds: Array.from(this._selectedIds) }——即当前全集,不是增量):

mapcadLayer.eventBus.on(VjmapextEvents.SelectionChanged, ({ selectedIds }) => {
  console.log('当前选中:', selectedIds);   // 全集;与 store.getSelectedIds() 一致
});

要拿"本次新增/移除"的增量,自己 diff 上一次的集合。

三、属性面板联动

mapcadLayer.eventBus.on(VjmapextEvents.SelectionChanged, () => {
  const ids = mapcadLayer.store.getSelectedIds();
  if (ids.length === 1) {
    showProp(mapcadLayer.store.get(ids[0]));
  } else if (ids.length > 1) {
    showBatchProp(ids);
  } else {
    hideProp();
  }
});

四、选中高亮

默认 vjmapext 选中后实体会有描边 / 夹点;不需要额外代码。

如果要自定义选中颜色:

let prev = new Set();
mapcadLayer.eventBus.on(VjmapextEvents.SelectionChanged, ({ selectedIds }) => {
  const now = new Set(selectedIds);
  // 新增选中
  for (const id of now) if (!prev.has(id)) {
    const e = mapcadLayer.store.get(id);
    e._origColor = e.color; e.color = 0xffff00; e.styleChanged();
  }
  // 取消选中
  for (const id of prev) if (!now.has(id)) {
    const e = mapcadLayer.store.get(id);
    if (e && e._origColor !== undefined) { e.color = e._origColor; e.styleChanged(); }
  }
  prev = now;
});

五、框选事件

触发条件:

  • 空闲态:必须 按住 Shift 从空白处拖才框选,且结果是 toggle(框内已选的取消、未选的加入);不按 Shift 的普通拖拽是平移地图
  • getSelections 等待中:从空白处直接拖即框选(追加;按 Shift 则 toggle)
  • getEntity({ multiple: true }) 等待中:从空白处拖即框选(toggle,受 filter 过滤)

方向:从左 → 右:窗口选(完全在框内);从右 → 左:交叉选(与框相交即可)。只选 editable 的实体。

mapcadLayer.eventBus.on(VjmapextEvents.BoxSelectStarted, () => {
  statusBar.textContent = '正在框选…';
});

mapcadLayer.eventBus.on(VjmapextEvents.BoxSelectEnded, () => {
  const ids = mapcadLayer.store.getSelectedIds();
  statusBar.textContent = `选中 ${ids.length} 个`;
});

顺序:先 selection:changed(选集已改完),后 boxselect:ended——所以在 boxselect:ended 里读 getSelectedIds() 拿到的已是框选后的结果。

六、编程修改选集

mapcadLayer.store.setSelection([id1, id2]);     // 覆盖全集
mapcadLayer.store.addToSelection(id3);        // 追加(单个 id)
mapcadLayer.store.removeFromSelection(id1);   // 取消(单个 id)
mapcadLayer.store.clearSelection();            // 清空

每次都会 fire selection:changed。

七、监听单击选中 + 空白取消

vjmapext 默认:

  • 单击实体 → 选中(setSelection([id]) 替换整个选集,不是追加)
  • 单击空白 → 清空选集(按住 Shift 点空白不清空)
  • Shift + 单击 → 追加 / 取消(toggleSelection;是 Shift 不是 Ctrl)
  • Shift + 从空白处拖 → 框选(toggle);不按 Shift 拖 = 平移地图
  • 编辑模式下 editable === false 的实体普通点选不中;需 Ctrl+Shift+单击 强制选中(浏览模式则看 selectableInReadonly)

这些都是内置逻辑,不用自己写;但组合 Shift 或"单选模式"需要自定义 → 覆写 click 处理。

八、批量删除按钮

mapcadLayer.eventBus.on(VjmapextEvents.SelectionChanged, () => {
  const ids = mapcadLayer.store.getSelectedIds();
  delBtn.disabled = ids.length === 0;
  delBtn.textContent = `删除 (${ids.length})`;
});

delBtn.onclick = () => {
  const ids = mapcadLayer.store.getSelectedIds();
  mapcadLayer.undoManager.beginTransaction();  // 删除选中
  ids.forEach(id => mapcadLayer.store.delete(id));
  mapcadLayer.undoManager.endTransaction();
};

九、过滤选集

// 只保留 LINE 类型
const ids = mapcadLayer.store.getSelectedIds()
  .filter(id => mapcadLayer.store.get(id).type === 'LINE');
mapcadLayer.store.setSelection(ids);

十、常见坑

  1. payload 是全集不是增量:selectedIds 是选后全集快照;要"新加/移除"得自己 diff。
  2. 频繁选集变化 + handler 昂贵:点击一片区域可能触发十几次;handler 加 debounce。
  3. 程序设选集触发事件:如果 handler 又 setSelection → 死循环;加 guard。
  4. 框选没结束:鼠标按下拖出去没抬起;BoxSelectEnded 不 fire → 检查 mouseup。
  5. 框选跨不同图层:只会选 mapcadLayer 里的实体,不选底图要素。

相关