事件使用
大约 4 分钟
事件使用
本章介绍如何监听和处理 WebCAD 事件。
基本事件监听
import { Engine, CadEvents } from 'vjcad';
// 通过 Engine.eventManager 访问事件管理器(推荐)
Engine.eventManager.on(CadEvents.EntityAdded, (args) => {
console.log('实体已添加:', args.entity.type, 'ID:', args.entity.id);
});
// 监听命令执行
Engine.eventManager.on(CadEvents.CommandStarted, (args) => {
console.log('命令开始执行:', args.commandName);
});
Engine.eventManager.on(CadEvents.CommandEnded, (args) => {
console.log('命令执行完成:', args.commandName);
});
// 一次性监听
Engine.eventManager.once(CadEvents.DocumentOpened, (args) => {
console.log('文档首次打开:', args.document.name);
});两种访问方式
// 方式一:通过 Engine(推荐)
import { Engine, CadEvents } from 'vjcad';
Engine.eventManager.on(CadEvents.EntityAdded, handler);
// 方式二:直接使用 CadEventManager
import { CadEventManager, CadEvents } from 'vjcad';
const events = CadEventManager.getInstance();
events.on(CadEvents.EntityAdded, handler);两种方式等价,Engine.eventManager 内部调用 CadEventManager.getInstance()。
可取消事件
部分事件支持取消操作:
// 阻止删除特定图层的实体
Engine.eventManager.on(CadEvents.EntityErasing, (args) => {
if (args.entity.layer === '保护图层') {
args.cancel = true; // 取消删除操作
console.log('禁止删除保护图层的实体');
}
});
// 阻止保存(例如验证失败时)
Engine.eventManager.on(CadEvents.DocumentSaving, (args) => {
const entities = Engine.getEntities();
if (entities.length === 0) {
args.cancel = true;
alert('文档为空,无法保存');
}
});
// 确认关闭
Engine.eventManager.on(CadEvents.DocumentClosing, (args) => {
if (args.document.DBMOD === 1) { // 有未保存修改
const confirmed = confirm('有未保存的修改,确定关闭?');
if (!confirmed) {
args.cancel = true;
}
}
});移除事件监听
// 保存handler引用以便移除
const entityHandler = (args) => {
console.log('实体添加:', args.entity.type);
};
// 注册
Engine.eventManager.on(CadEvents.EntityAdded, entityHandler);
// 移除
Engine.eventManager.off(CadEvents.EntityAdded, entityHandler);
// 移除指定事件的所有监听器
Engine.eventManager.offAll(CadEvents.EntityAdded);
// 移除所有事件的监听器
Engine.eventManager.offAll();批量操作时暂停事件
// 批量操作时暂停事件触发(提高性能)
Engine.eventManager.suspendEvents();
try {
// 执行大量操作...
for (let i = 0; i < 1000; i++) {
const line = new LineEnt(...);
Engine.pcanvas.addEntity(line);
}
} finally {
Engine.eventManager.resumeEvents();
}
// 检查状态
if (Engine.eventManager.isSuspended()) {
console.log('事件已暂停');
}事件参数类型
import { Engine, CadEvents } from 'vjcad';
import type {
EntityEventArgs,
DocumentEventArgs,
CommandEventArgs,
CancellableEventArgs
} from 'vjcad';
// 实体事件参数
Engine.eventManager.on(CadEvents.EntityAdded, (args: EntityEventArgs) => {
const entity = args.entity;
const timestamp = args.timestamp;
});
// 文档事件参数
Engine.eventManager.on(CadEvents.DocumentOpened, (args: DocumentEventArgs) => {
const doc = args.document;
});
// 命令事件参数
Engine.eventManager.on(CadEvents.CommandStarted, (args: CommandEventArgs) => {
const cmdName = args.commandName;
});
// 可取消事件参数
Engine.eventManager.on(CadEvents.EntityErasing, (args: CancellableEventArgs & EntityEventArgs) => {
args.cancel = true; // 取消操作
});Engine.eventManager API
| 方法 | 说明 |
|---|---|
on(event, handler) | 监听事件 |
off(event, handler) | 取消监听 |
once(event, handler) | 一次性监听 |
offAll(event?) | 移除所有监听器 |
emit(event, args) | 触发事件(内部使用) |
suspendEvents() | 暂停事件 |
resumeEvents() | 恢复事件 |
isSuspended() | 检查是否暂停 |
hasListeners(event) | 检查是否有监听器 |
listenerCount(event) | 获取监听器数量 |
使用场景
自动保存
Engine.eventManager.on(CadEvents.DocumentModified, (args) => {
scheduleAutoSave();
});操作日志
Engine.eventManager.on(CadEvents.CommandEnded, (args) => {
log(`用户执行了命令: ${args.commandName}`);
});实体验证
Engine.eventManager.on(CadEvents.EntityAdding, (args) => {
if (!validateEntity(args.entity)) {
args.cancel = true;
}
});选择变化响应
Engine.eventManager.on(CadEvents.SelectionChanged, (args) => {
updatePropertyPanel(args.selection);
});交互式命令的事件时序
重要:PLINE 等交互式命令的事件触发时机
交互式绘制命令(如 PLINE、SPLINE)在用户输入过程中会分阶段触发事件。以 PLINE 命令为例:
- 用户点击第 2 个点时 → 创建实体 → 触发
EntityAdded(此时实体仅包含 2 个点) - 用户点击第 3、4、… 个点时 → 修改已有实体 → 触发
EntityModified - 用户按回车结束时 → 触发
CommandEnded(此时实体包含全部点位)
如果在 EntityAdded 回调中读取点位,只能获取到最初的 2 个点,而非完整数据。
// ❌ 错误做法:在 EntityAdded 中获取完整点位
Engine.eventManager.on(CadEvents.EntityAdded, (args) => {
if (args.entity.type === 'PLINE') {
// 此时只有前 2 个点,后续点还没输入!
console.log(args.entity.bulgePoints.items.length); // 输出: 2
}
});
// ✅ 正确做法一:在 CommandEnded 中获取完整数据
Engine.eventManager.on(CadEvents.CommandEnded, (args) => {
if (args.commandName === 'PLINE') {
const plines = Engine.getEntities(e => e.type === 'PLINE');
const lastPline = plines[plines.length - 1];
console.log(lastPline.bulgePoints.items.length); // 输出: 全部点数
}
});
// ✅ 正确做法二:实时跟踪点位变化
Engine.eventManager.on(CadEvents.EntityModified, (args) => {
if (args.entity.type === 'PLINE') {
console.log('当前点数:', args.entity.bulgePoints.items.length);
}
});从实体与 toDb() 读取点位的对照
实体对象和 toDb() 序列化数据的点位结构不同,请参考下表:
| 属性 | 实体对象 | toDb() 输出 |
|---|---|---|
| 点位集合 | entity.bulgePoints.items | dbPline.dbBulgePoints.pts |
| 单个点坐标 | bp.point2d.x / .y | pts[i][0] / pts[i][1] |
| 凸度 | bp.bulge | bulges?[i](全 0 时省略) |
// 从 toDb() 读取点位
const dbDoc = Engine.currentDoc.toDb();
const modelBlock = dbDoc.dbBlocks['*Model'];
const dbPline = modelBlock.items.find(item => item.type === 'PLINE');
if (dbPline) {
const pts = dbPline.dbBulgePoints.pts; // [[x,y], [x,y], ...]
const bulges = dbPline.dbBulgePoints.bulges; // [b1, b2, ...] or undefined
pts.forEach((pt, i) => {
const x = pt[0], y = pt[1];
const bulge = bulges ? bulges[i] : 0;
console.log(`点${i}: (${x}, ${y}), bulge=${bulge}`);
});
}
// 从 DB 数据还原实体
const newPline = new PolylineEnt(new BulgePoints());
newPline.fromDb(dbPline);在线示例
完整的交互式演示请参考 多段线点位与事件时序{target="_blank"}