ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

lowcode-engine 拖拽引擎 Dragon 完全指南:IPublicModelDragon 属性、事件与投放感应机制解析

2026/9/14 22:17:49 拓冰建站 浏览量
lowcode-engine 拖拽引擎 Dragon 完全指南:IPublicModelDragon 属性、事件与投放感应机制解析 lowcode-engine 拖拽引擎 Dragon 完全指南IPublicModelDragon 属性、事件与投放感应机制解析【免费下载链接】lowcode-engineAn enterprise-class low-code technology stack with scale-out design / 一套面向扩展设计的企业级低代码技术体系项目地址: https://gitcode.com/GitHub_Trending/lo/lowcode-engine导读Dragon是 lowcode-engine 设计器中负责**拖拽Drag-and-Drop**的核心对象承载了组件面板拖入画布、画布内节点移动、跨窗口拖拽与投放感应Sensor定位等全部拖拽链路。本文以官方 API 文档 dragon.md 为主体结合 Dragon 类实现源码、Shell 层封装 与 单元测试用例完整讲解IPublicModelDragon的每个属性与方法的签名、语义、底层行为帮助你读懂并正确使用这套拖拽 API。基本介绍Dragon即拖拽对象是整个设计器拖拽体系的入口。在设计器中无论是从组件面板把某个物料拖到画布还是把画布中的节点从一个容器拖到另一个容器最终都会汇入Dragon统一管理它负责监听鼠标事件、判定抖动是否达到拖拽阈值、生成LocateEvent、路由给对应的投放感应区Sensor完成定位并在拖拽结束时派发dragend事件。对应接口该文档对应的 TypeScript 接口为IPublicModelDragon位于 types 包引入方式与文档一致import { IPublicModelDragon } from alilc/lowcode-types;支持版本sincev1.0.0接口定义层面sincev1.1.0当前文档所述能力接口与实现的对应关系IPublicModelDragon只是面向 Shell 层的公开 API 定义真正的拖拽引擎实现在 designer 包 中的Dragon类注释明确标注为 Drag-on 拖拽引擎其声明为export interface IDragon extends IPublicModelDragonINode, ILocateEvent { emitter: IEventBus; } export class Dragon implements IDragon { // ... }从源码结构看内部实现还额外暴露了emitter模块事件总线事件名为Dragon以及activeSensor、key、viewName等内部成员这些并不属于公开 API但正是理解各公开方法底层行为的关键。属性draggingdragging用于判断当前是否正处于拖拽状态/** * is dragging or not */ get dragging(): boolean;在 Dragon 类实现 中该属性由一个obx.ref可观察引用_dragging支撑具备响应式能力obx.ref private _dragging false; get dragging(): boolean { return this._dragging; }使用要点_dragging初始为false只有当鼠标移动被判定为已抖动见下文isShaken抖动判定并触发dragstart后才在dragstart()内部被置为true在 测试用例 中可以看到expect(dragon.dragging).toBeTruthy()的断言——鼠标移动超过阈值后dragging即为真由于它是可观察属性可以驱动 UI 层如拖拽态光标、感应区高亮的响应式更新。方法事件绑定三件套onDragstart绑定 dragstart 事件/** * 绑定 dragstart 事件 * bind a callback function which will be called on dragging start * param func * returns */ onDragstart(func: (e: IPublicModelLocateEvent) any): () void;触发时机拖拽真正开始判定为抖动后触发一次回调参数IPublicModelLocateEvent定位事件返回值一个解绑函数调用后即移除监听。onDrag绑定 drag 事件/** * 绑定 drag 事件 * bind a callback function which will be called on dragging * param func * returns */ onDrag(func: (e: IPublicModelLocateEvent) any): () void;触发时机拖拽进行中每次鼠标移动或原生dragover都会触发注意回调中拿到的是尚未经过 Sensor 订正的LocateEventcanvasX/canvasY为空如需精确坐标需等待 Sensor 的fixEvent订正见下文LocateEvent 结构一节。onDragend绑定 dragend 事件/** * 绑定 dragend 事件 * bind a callback function which will be called on dragging end * param func * returns */ onDragend(func: (o: { dragObject: IPublicModelDragObject; copy?: boolean }) any): () void;触发时机拖拽结束mouseup或原生dragend时触发回调参数一个包含dragObject拖拽对象和copy是否为复制拖拽的对象重要copy仅在按住Alt/Ctrl键或从组件面板发起的 NodeData 拖拽时为true否则为false。底层实现事件总线的封装三个事件方法在 Dragon 类实现 中全部基于内部emittercreateModuleEventBus(Dragon)封装onDragstart(func: (e: ILocateEvent) any) { this.emitter.on(dragstart, func); return () { this.emitter.removeListener(dragstart, func); }; } // onDrag / onDragend 同理分别监听 drag / dragend在 Shell 层封装 中回调收到的内部ILocateEvent、DragObject还会被转换成面向用户的可读模型LocateEvent.create(e)、DragObject.create(o.dragObject)确保公开 API 的参数类型稳定。测试用例中的典型用法测试用例 给出了标准的事件注册 拖拽触发 解绑流程const offDragStart dragon.onDragstart(dragStartMockFn); const offDrag dragon.onDrag(dragMockFn); const offDragEnd dragon.onDragend(dragEndMockFn); dragon.boost( { type: IPublicEnumDragObjectType.NodeData, data: [{ componentName: Button }], }, new MouseEvent(mousedown, { clientX: 100, clientY: 100 }), ); fireEvent.mouseMove(document, { clientX: 108, clientY: 108 }); fireEvent.mouseMove(document, { clientX: 110, clientY: 110 }); fireEvent.mouseUp(document, { clientX: 118, clientY: 118 }); expect(dragStartMockFn).toHaveBeenCalledTimes(1); expect(dragMockFn).toHaveBeenCalledTimes(2); expect(dragEndMockFn).toHaveBeenCalledTimes(1);注意一个细节boost之后如果立即mouseUp未发生位移dragstart/drag都不会触发只有移动超过抖动阈值源码中SHAKE_DISTANCE 4像素后拖拽才真正开始。方法from 与 boost —— 拖拽的入口from设置拖拽监听区域与转换函数/** * 设置拖拽监听的区域 shell以及自定义拖拽转换函数 boost * set a html element as shell to dragon as monitoring target, and * set boost function which is used to transform a MouseEvent to type * IPublicTypeDragNodeDataObject. * param shell 拖拽监听的区域 * param boost 拖拽转换函数 */ from(shell: Element, boost: (e: MouseEvent) IPublicTypeDragNodeDataObject | null): any;shell要监听的 HTML 区域元素通常是组件面板容器boost自定义转换函数把一次MouseEvent转换为拖拽对象返回null表示本次不发起拖拽例如点击的是无效区域返回值同样是解绑函数可用于移除监听。底层行为源码实现from会在shell上注册一个mousedown监听先做两重过滤const mousedown (e: MouseEvent) { // ESC or RightClick if (e.which 3 || e.button 2) { return; // 右键不触发拖拽 } // Get a new node to be dragged const dragObject boost(e); if (!dragObject) { return; // boost 返回 null 不触发拖拽 } this.boost(dragObject, e); };测试用例 验证了这两条过滤路径mouseDown传入{ button: 2 }右键或boost返回null时dragStartMockFn均不会被调用。boost发射拖拽对象/** * 发射拖拽对象 * boost your dragObject for dragging(flying) * * param dragObject 拖拽对象 * param boostEvent 拖拽初始时事件 */ boost(dragObject: IPublicTypeDragObject, boostEvent: MouseEvent | DragEvent, fromRglNode?: IPublicModelNode): void;dragObject拖拽对象IPublicTypeDragObject是三种拖拽对象的联合类型定义见 types 包IPublicTypeDragNodeObject拖拽已有节点携带nodes: IPublicModelNode[]IPublicTypeDragNodeDataObject拖拽节点数据Schema携带data: IPublicTypeNodeSchema | IPublicTypeNodeSchema[]可选thumbnail、description字段见 drag-node-data-object.ts多用于组件面板拖物料IPublicTypeDragAnyObject拖拽任意自定义对象。boostEvent拖拽的初始事件MouseEvent或原生DragEventfromRglNode可选来自 React-Grid-Layout 场景的源节点用于 RGL 内拖拽联动。boost 内部的关键流程boost是拖拽引擎的核心方法其内部流程源码实现可以概括为以下几个阶段初始化收集主 Sensor所有激活文档的 simulator、用makeEventsHandler绑定事件处理上下文并区分新拖拽NodeData与节点拖拽Node新拖拽默认强制走 copy 态抖动判定move处理器通过isShaken(boostEvent, e)判断是否开始拖拽——当移动距离超过SHAKE_DISTANCE4px或target变化时视为抖动从而触发dragstart()否则dragging保持falseexport function isShaken(e1, e2) { if ((e1 as any).shaken) return true; if (e1.target ! e2.target) return true; return ( Math.pow(e1.clientY - e2.clientY, 2) Math.pow(e1.clientX - e2.clientX, 2) SHAKE_DISTANCE ); }拷贝态控制checkcopy监听Alt/Ctrl键按下时把copy置为true并通过setCopyState通知 simulator 切换光标拖拽 NodeData 时通过dataTransfer.dropEffect copy标记复制语义事件路由每次移动都会createLocateEvent生成LocateEvent再由chooseSensor从this.sensors与 master sensors 中挑选命中的 Sensor调用其fixEventlocate完成投放定位同时向emitter派发drag事件结束清理over在mouseup/dragend时恢复原生选区与光标状态、移除事件监听、派发dragend若拖拽过程中按下ESCkeyCode 27则调用designer.clearLocation()取消拖拽测试用例mouse Node esc验证了该行为。方法Sensor 投放感应区管理addSensor添加投放感应区/** * 添加投放感应区 * add sensor area */ addSensor(sensor: any): void;将外部定义的 Sensor 注册进Dragon的传感器列表使其参与后续拖拽的命中判定。实现上就是推入内部数组addSensor(sensor: any) { this.sensors.push(sensor); }removeSensor移除投放感应/** * 移除投放感应 * remove sensor area */ removeSensor(sensor: any): void;将 Sensor 从列表中移除通过indexOf找到并splice移除后该区域不再响应拖拽定位。什么是 SensoraddSensor/removeSensor中的sensor对应IPublicModelSensor定义见 types 包即拖拽敏感板。设计器画布、面板等投放区域都通过实现该接口参与拖拽定位核心能力包括sensorAvailable是否可响应拖拽如面板隐藏时返回falsefixEvent(e)给LocateEvent打补丁补全canvasX/canvasY等画布坐标并将fixed置为truelocate(e)定位并激活返回IPublicModelDropLocation投放位置见 drop-location.ts包含target、detail、event等isEnter(e)判断事件点是否进入本敏感板区域deactiveSensor()取消激活getNodeInstanceFromElement?由元素反查节点实例。附LocateEvent 定位事件结构onDragstart/onDrag回调收到的IPublicModelLocateEvent定义见 locate-event.ts是理解整个拖拽定位机制的关键数据结构export interface IPublicModelLocateEvent { get type(): string; // LocateEvent readonly globalX: number; // 浏览器窗口坐标系 X readonly globalY: number; // 浏览器窗口坐标系 Y readonly originalEvent: MouseEvent | DragEvent; // 原始事件 target?: Element | null; // 浏览器事件响应目标 canvasX?: number; // 画布坐标系 X需 Sensor.fixEvent 订正 canvasY?: number; // 画布坐标系 Y fixed?: true; // 是否已订正初始从发起端构造时无 canvasX/canvasY documentModel?: IPublicModelDocumentModel | null; // 激活或目标文档 get dragObject(): IPublicModelDragObject | null; // 当前拖拽对象 }在内部实现ILocateEventdragon.ts中还扩展了sensor字段用于标记当前激活的感应区。当事件来自 simulator 沙箱iframe时createLocateEvent会通过srcSim.viewport.toGlobalPoint(e)把 iframe 内坐标换算为浏览器全局坐标保证跨窗口拖拽的定位一致性——这也是 lowcode-engine 支持从侧边面板拖到 iframe 画布的关键实现细节。小结API类型说明关键参数dragging属性是否正在拖拽响应式—onDragstart(func)方法绑定拖拽开始事件(e: IPublicModelLocateEvent) anyonDrag(func)方法绑定拖拽进行事件(e: IPublicModelLocateEvent) anyonDragend(func)方法绑定拖拽结束事件({ dragObject, copy }) anyfrom(shell, boost)方法监听区域并转换拖拽对象Element,(e) IPublicTypeDragNodeDataObject \| nullboost(dragObject, boostEvent, fromRglNode?)方法发射拖拽对象IPublicTypeDragObject,MouseEvent \| DragEvent,IPublicModelNode?addSensor(sensor)/removeSensor(sensor)方法注册/注销投放感应区IPublicModelSensor三个事件方法的返回值均为解绑函数适合在组件卸载时调用以避免内存泄漏boost配合from是最常见的两种拖拽发起方式面板拖物料用from NodeData 对象画布内挪节点直接boostNode 对象。如需深入了解 Sensor 定位、DropLocation 投放位置的完整语义可继续阅读 sensor.ts、drop-location.ts 以及 Dragon 类完整实现。【免费下载链接】lowcode-engineAn enterprise-class low-code technology stack with scale-out design / 一套面向扩展设计的企业级低代码技术体系项目地址: https://gitcode.com/GitHub_Trending/lo/lowcode-engine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考