bus-selection
本文回答:选集事件 ——
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);
十、常见坑
- payload 是全集不是增量:
selectedIds是选后全集快照;要"新加/移除"得自己 diff。 - 频繁选集变化 + handler 昂贵:点击一片区域可能触发十几次;handler 加 debounce。
- 程序设选集触发事件:如果 handler 又 setSelection → 死循环;加 guard。
- 框选没结束:鼠标按下拖出去没抬起;BoxSelectEnded 不 fire → 检查 mouseup。
- 框选跨不同图层:只会选 mapcadLayer 里的实体,不选底图要素。
相关
- 实体事件 → bus-entity
- 命令事件 → bus-command-mode
- 输入选集 → drawing/input-manager