尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

G6 事件系统完全指南:事件监听、常量枚举与生命周期钩子的实战解析

G6 事件系统完全指南:事件监听、常量枚举与生命周期钩子的实战解析 数据可视化前端图表库【免费下载链接】G6♾ A Graph Visualization Framework in JavaScript.项目地址https://gitcode.com/gh_mirrors/g6/G6点击查看免费下载G6AntV G6JavaScript 图可视化框架内置了一套功能强大的事件机制让开发者可以响应图中发生的各类交互行为——从节点点击、边悬停到画布拖拽、键盘输入再到渲染、布局、动画等生命周期阶段。本文以官方 API 文档为基础结合仓库源码系统讲解 G6 事件系统的分类、命名规范、Graph事件 API 的完整用法、全部事件常量枚举并从源码层面揭示事件从底层 DOM 转发到graph.on回调的完整链路帮助你在实际项目中写出类型安全、可维护的交互逻辑。事件系统概览G6 的事件系统允许开发者响应图表中发生的各种交互行为例如节点点击、边悬停、画布拖拽等。通过事件系统你可以实现复杂的交互逻辑显著提升用户体验。事件分类G6 中的事件可以大致分为以下三类元素事件Element Events与节点node、边edge和 Combo 相关的事件例如node:click、edge:mouseenter画布事件Canvas Events与整个画布相关的事件例如canvas:drag、canvas:wheel生命周期事件Lifecycle Events与图表生命周期相关的事件例如beforerender、afterrender事件命名规范G6 事件遵循[object]:[event]的格式例如node:click—— 节点点击事件edge:mouseenter—— 鼠标移入边事件canvas:drag—— 画布拖拽事件其中[object]部分是事件作用的目标对象类型node / edge / combo / canvas[event]部分是具体的交互行为。这种命名方式让事件的意图一目了然也便于按对象类型统一管理监听器。最佳实践使用常量枚举G6 提供了一套完整的事件常量枚举官方强烈建议使用这些常量而非直接书写字符串事件名import { NodeEvent, EdgeEvent, CanvasEvent, GraphEvent } from antv/g6; // 使用常量枚举监听事件 graph.on(NodeEvent.CLICK, handleNodeClick); graph.on(EdgeEvent.POINTER_OVER, handleEdgeHover); graph.on(CanvasEvent.DRAG, handleCanvasDrag); graph.on(GraphEvent.AFTER_RENDER, handleAfterRender);优势类型安全避免字符串拼写错误提供智能代码提示和自动补全在仓库源码中这些枚举定义在 packages/g6/src/constants/events 目录下例如 node.ts 中NodeEvent.CLICK的取值即为字符串node:click。由于枚举值本质上是字符串字面量即便不使用枚举而直接传字符串也能正常工作但枚举能借助 TypeScript 的类型系统在编译期拦截拼写错误并让 IDE 提供补全提示这是大型项目中推荐使用枚举的根本原因。API 参考Graph 事件 APIGraph类提供了on、once、off三个事件 API。在源码层面Graph类继承自antv/event-emitter见 graph.ts并在 graph.ts 中对这三个方法进行了类型化重写与文档化声明因此它们具备完整的 TypeScript 泛型支持。Graph.on(eventName, callback, once)监听指定事件当事件触发时执行回调函数。onT extends IEvent IEvent(eventName: string, callback: (event: T) void, once?: boolean): this;参数参数说明类型默认值必填eventName要监听的事件名称string-✓callback事件触发时执行的回调函数(event: T) void-✓once是否只监听一次boolean-返回值类型thisGraph 实例说明返回图实例本身支持链式调用示例import { NodeEvent, EdgeEvent, CanvasEvent } from antv/g6; // 监听节点点击事件 graph.on(NodeEvent.CLICK, (evt) { const { target } evt; // 获取被点击节点的 ID console.log(Node ${target.id} was clicked); // 获取节点数据 const nodeData graph.getNodeData(target.id); console.log(Node data:, nodeData); // 修改节点状态 graph.setElementState(target.id, selected); }); // 监听边鼠标移入事件 graph.on(EdgeEvent.POINTER_OVER, (evt) { const { target } evt; graph.setElementState(target.id, highlight); }); // 监听画布拖拽事件 graph.on(CanvasEvent.DRAG, (evt) { console.log(Canvas is being dragged); });Graph.once(eventName, callback)只监听一次事件事件触发一次后自动移除监听器。onceT extends IEvent IEvent(eventName: string, callback: (event: T) void): this;参数参数说明类型默认值必填eventName要监听的事件名称string-✓callback事件触发时执行的回调函数(event: T) void-✓返回值类型thisGraph 实例说明返回图实例本身支持链式调用示例import { GraphEvent, NodeEvent } from antv/g6; // 监听图表首次加载完成事件仅执行一次 graph.once(GraphEvent.AFTER_RENDER, () { console.log(Chart rendered for the first time); // 执行一次性初始化操作 highlightImportantNodes(); }); // 等待用户第一次点击节点后再执行操作 graph.once(NodeEvent.CLICK, (evt) { console.log(User clicked a node for the first time:, evt.target.id); showTutorialTip(You can drag nodes to change their position); });once非常适合首屏初始化引导、一次性数据加载、新手教程提示等只应发生一次的场景。Graph.off()移除所有事件监听器。off(): this;返回值类型thisGraph 实例说明返回图实例本身支持链式调用示例// 移除所有事件监听器 graph.off(); console.log(All event listeners have been removed);注意graph.destroy()销毁图实例时也会自动调用this.off()清理监听器见 graph.ts因此通常无需手动全量移除但当你需要复用同一个Graph实例、清空旧交互时off()是一个干净利落的入口。Graph.off(eventName)移除指定事件类型的全部监听器。off(eventName: string): this;参数参数说明类型默认值必填eventName要移除的事件名称string-✓返回值类型thisGraph 实例说明返回图实例本身支持链式调用示例import { NodeEvent } from antv/g6; // 移除所有节点点击事件监听器 graph.off(NodeEvent.CLICK); console.log(All node click event listeners have been removed); // 在某种操作模式结束后移除相关的临时事件监听器 function exitEditMode() { // 移除编辑模式下的所有监听器 graph.off(NodeEvent.DRAG_END); graph.off(NodeEvent.DROP); console.log(Exited edit mode); }Graph.off(eventName, callback)移除指定事件的指定回调函数。off(eventName: string, callback: (...args: any[]) void): this;参数参数说明类型默认值必填eventName要移除的事件名称string-✓callback要移除的回调函数(...args: any[]) void-✓返回值类型thisGraph 实例说明返回图实例本身支持链式调用示例import { NodeEvent } from antv/g6; // 定义回调函数 const handleNodeClick (evt) { console.log(Node clicked:, evt.target.id); }; // 添加监听器 graph.on(NodeEvent.CLICK, handleNodeClick); // 稍后在某个时机移除这个特定的监听器 graph.off(NodeEvent.CLICK, handleNodeClick); console.log(Specific node click event listener has been removed);使用要点off(eventName, callback)只移除传入的特定回调引用因此必须保证传入的函数与注册时的函数是同一个引用。如果你在注册时使用了匿名箭头函数如graph.on(NodeEvent.CLICK, (evt) {...})将无法通过这种方式单独移除它——这正是推荐将回调提取为具名函数的原因。事件常量枚举全表G6 提供了多种事件常量枚举便于开发者使用标准化的事件名称。以下为全部事件常量的详细说明。节点事件NodeEvent常量名事件名说明CLICKnode:click点击节点时触发DBLCLICKnode:dblclick双击节点时触发POINTER_OVERnode:pointerover指针移入节点时触发POINTER_LEAVEnode:pointerleave指针移出节点时触发POINTER_ENTERnode:pointerenter指针移入节点或其子元素时触发不冒泡POINTER_MOVEnode:pointermove指针在节点上移动时触发POINTER_OUTnode:pointerout指针移出节点时触发POINTER_DOWNnode:pointerdown指针在节点上按下时触发POINTER_UPnode:pointerup指针在节点上抬起时触发CONTEXT_MENUnode:contextmenu在节点上打开上下文菜单时触发DRAG_STARTnode:dragstart开始拖拽节点时触发DRAGnode:drag节点拖拽过程中触发DRAG_ENDnode:dragend节点拖拽结束时触发DRAG_ENTERnode:dragenter可拖拽项进入节点时触发DRAG_OVERnode:dragover可拖拽项位于节点上方时触发DRAG_LEAVEnode:dragleave可拖拽项离开节点时触发DROPnode:drop可拖拽项在节点上放下时触发边事件EdgeEvent常量名事件名说明CLICKedge:click点击边时触发DBLCLICKedge:dblclick双击边时触发POINTER_OVERedge:pointerover指针移入边时触发POINTER_LEAVEedge:pointerleave指针移出边时触发POINTER_ENTERedge:pointerenter指针移入边或其子元素时触发不冒泡POINTER_MOVEedge:pointermove指针在边上移动时触发POINTER_OUTedge:pointerout指针移出边时触发POINTER_DOWNedge:pointerdown指针在边上按下时触发POINTER_UPedge:pointerup指针在边上抬起时触发CONTEXT_MENUedge:contextmenu在边上打开上下文菜单时触发DRAG_ENTERedge:dragenter可拖拽项进入边时触发DRAG_OVERedge:dragover可拖拽项位于边上方时触发DRAG_LEAVEedge:dragleave可拖拽项离开边时触发DROPedge:drop可拖拽项在边上放下时触发Combo 事件ComboEvent常量名事件名说明CLICKcombo:click点击 Combo 时触发DBLCLICKcombo:dblclick双击 Combo 时触发POINTER_OVERcombo:pointerover指针移入 Combo 时触发POINTER_LEAVEcombo:pointerleave指针移出 Combo 时触发POINTER_ENTERcombo:pointerenter指针移入 Combo 或其子元素时触发不冒泡POINTER_MOVEcombo:pointermove指针在 Combo 上移动时触发POINTER_OUTcombo:pointerout指针移出 Combo 时触发POINTER_DOWNcombo:pointerdown指针在 Combo 上按下时触发POINTER_UPcombo:pointerup指针在 Combo 上抬起时触发CONTEXT_MENUcombo:contextmenu在 Combo 上打开上下文菜单时触发DRAG_STARTcombo:dragstart开始拖拽 Combo 时触发DRAGcombo:dragCombo 拖拽过程中触发DRAG_ENDcombo:dragendCombo 拖拽结束时触发DRAG_ENTERcombo:dragenter可拖拽项进入 Combo 时触发DRAG_OVERcombo:dragover可拖拽项位于 Combo 上方时触发DRAG_LEAVEcombo:dragleave可拖拽项离开 Combo 时触发DROPcombo:drop可拖拽项在 Combo 上放下时触发画布事件CanvasEvent常量名事件名说明CLICKcanvas:click点击画布空白区域时触发DBLCLICKcanvas:dblclick双击画布空白区域时触发POINTER_OVERcanvas:pointerover指针移入画布时触发POINTER_LEAVEcanvas:pointerleave指针移出画布时触发POINTER_ENTERcanvas:pointerenter指针移入画布或其子元素时触发不冒泡POINTER_MOVEcanvas:pointermove指针在画布上移动时触发POINTER_OUTcanvas:pointerout指针移出画布时触发POINTER_DOWNcanvas:pointerdown指针在画布上按下时触发POINTER_UPcanvas:pointerup指针在画布上抬起时触发CONTEXT_MENUcanvas:contextmenu在画布上打开上下文菜单时触发DRAG_STARTcanvas:dragstart开始拖拽画布时触发DRAGcanvas:drag画布拖拽过程中触发DRAG_ENDcanvas:dragend画布拖拽结束时触发DRAG_ENTERcanvas:dragenter可拖拽项进入画布时触发DRAG_OVERcanvas:dragover可拖拽项位于画布上方时触发DRAG_LEAVEcanvas:dragleave可拖拽项离开画布时触发DROPcanvas:drop可拖拽项在画布上放下时触发WHEELcanvas:wheel在画布上滚动鼠标滚轮时触发图生命周期事件GraphEvent常量名事件名说明BEFORE_CANVAS_INITbeforecanvasinit画布初始化之前触发AFTER_CANVAS_INITaftercanvasinit画布初始化之后触发BEFORE_SIZE_CHANGEbeforesizechange视口尺寸变更之前触发AFTER_SIZE_CHANGEaftersizechange视口尺寸变更之后触发BEFORE_ELEMENT_CREATEbeforeelementcreate元素创建之前触发AFTER_ELEMENT_CREATEafterelementcreate元素创建之后触发BEFORE_ELEMENT_UPDATEbeforeelementupdate元素更新之前触发AFTER_ELEMENT_UPDATEafterelementupdate元素更新之后触发BEFORE_ELEMENT_DESTROYbeforeelementdestroy元素销毁之前触发AFTER_ELEMENT_DESTROYafterelementdestroy元素销毁之后触发BEFORE_ELEMENT_TRANSLATEbeforeelementtranslate元素平移之前触发AFTER_ELEMENT_TRANSLATEafterelementtranslate元素平移之后触发BEFORE_DRAWbeforedraw开始绘制之前触发AFTER_DRAWafterdraw绘制结束之后触发BEFORE_RENDERbeforerender开始渲染之前触发AFTER_RENDERafterrender渲染完成后触发BEFORE_ANIMATEbeforeanimate动画开始之前触发AFTER_ANIMATEafteranimate动画结束之后触发BEFORE_LAYOUTbeforelayout布局开始之前触发AFTER_LAYOUTafterlayout布局结束之后触发BEFORE_STAGE_LAYOUTbeforestagelayout管道式布局每个阶段开始前触发AFTER_STAGE_LAYOUTafterstagelayout管道式布局每个阶段结束后触发BEFORE_TRANSFORMbeforetransform视口变换之前触发AFTER_TRANSFORMaftertransform视口变换之后触发BATCH_STARTbatchstart批量操作开始时触发BATCH_ENDbatchend批量操作结束时触发BEFORE_DESTROYbeforedestroy图表销毁之前触发AFTER_DESTROYafterdestroy图表销毁之后触发BEFORE_RENDERER_CHANGEbeforerendererchange渲染器切换之前触发AFTER_RENDERER_CHANGEafterrendererchange渲染器切换之后触发容器事件ContainerEvent常量名事件名说明KEY_DOWNkeydown按下键盘按键时触发KEY_UPkeyup松开键盘按键时触发通用事件CommonEvent以下为不带前缀的事件可用于监听全局事件常量名事件名说明CLICKclick点击任意元素时触发DBLCLICKdblclick双击任意元素时触发POINTER_OVERpointerover指针移入任意元素时触发POINTER_LEAVEpointerleave指针移出任意元素时触发POINTER_ENTERpointerenter指针移入任意元素或其子元素时触发不冒泡POINTER_MOVEpointermove指针在任意元素上移动时触发POINTER_OUTpointerout指针移出任意元素时触发POINTER_DOWNpointerdown指针在任意元素上按下时触发POINTER_UPpointerup指针在任意元素上抬起时触发CONTEXT_MENUcontextmenu在任意元素上打开上下文菜单时触发DRAG_STARTdragstart开始拖拽任意元素时触发DRAGdrag拖拽任意元素过程中触发DRAG_ENDdragend拖拽任意元素结束时触发DRAG_ENTERdragenter可拖拽项进入任意元素时触发DRAG_OVERdragover可拖拽项位于任意元素上方时触发DRAG_LEAVEdragleave可拖拽项离开任意元素时触发DROPdrop可拖拽项在任意元素上放下时触发KEY_DOWNkeydown按下键盘按键时触发KEY_UPkeyup松开键盘按键时触发WHEELwheel滚动鼠标滚轮时触发PINCHpinch多点触屏上双指捏合或张开时触发从源码看事件系统是如何工作的理解了 API 之后我们深入源码理解事件系统的底层实现这有助于你在排查问题和编写自定义交互时做到心中有数。事件监听器的承载者EventEmitterGraph类在 graph.ts 中export class Graph extends EventEmitter直接继承自antv/event-emitter。on、once、off的核心能力监听器存储、触发、移除均由基类提供Graph只是在此基础上补充了泛型类型签名onT extends IEvent IEvent与 JSDoc 文档。这也是on/once/off三个方法都返回this、天然支持链式调用的原因。事件从浏览器到 graph.on 的转发链路当你在页面上点击一个节点时浏览器产生原生事件G6 内部负责把原生事件翻译成node:click这样的 G6 事件。这条链路的核心在 behavior.ts 的BehaviorController中绑定底层监听forwardEvents()在容器container上监听keydown、keyup对应ContainerEvent在画布文档canvas document上监听click、pointermove、drag、wheel等全部CommonEvent事件见 behavior.ts。归一化事件对象forwardCanvasEvents回调拿到antv/g的FederatedEvent后调用eventTargetOf(originalTarget)判断事件命中的目标。该工具函数在 utils/event/index.ts 中实现它会从命中的图形DisplayObject开始沿父级链向上查找直到找到属于 node / edge / combo 的元素返回{ type: node | edge | combo | canvas, element }若都没有命中则视为canvasDocument实例。组装标准事件并双发随后构造stdEvent { ...event, target: targetElement, targetType, originalTarget }并执行关键的双发逻辑见 behavior.ts// 非右键点击事件 graph.emit(${targetType}:${type}, stdEvent); // 例如 node:click graph.emit(type, stdEvent); // 例如 click全局事件也就是说一次原生交互会同时触发两个事件带前缀的元素/画布事件如node:click和不带前缀的全局事件如click。这正是CommonEvent能实现事件委托的底层依据——无论点击的是节点、边还是画布空白区全局click都会触发而evt.targetType帮你区分具体命中的对象类型。特殊事件的处理源码中还处理了两个边界情况——双击click且detail 2时额外派发dblclick与右键菜单pointerdown且button 2时派发contextmenu并注入preventDefault方法见 behavior.ts。生命周期事件是怎么发出的与交互事件不同生命周期事件由 G6 内部各个运行时模块在关键节点主动调用emit触发。例如在 element.ts 中元素创建、更新、销毁的 before/after 事件分别通过ElementLifeCycleEvent发出绘制前后通过GraphLifeCycleEvent发出element.ts。这些事件类的定义集中在 utils/event/events.tsGraphLifeCycleEvent承载beforeRender、afterRender、beforeDraw、afterDraw、beforeLayout、afterLayout、batchStart、batchEnd、beforeDestroy、afterDestroy等附带data字段AnimateEvent承载beforeAnimate/afterAnimate附带animationType、animation对象ElementLifeCycleEvent承载元素创建/更新/销毁的前后事件附带elementType与dataViewportEvent承载视口变换的前后事件附带TransformOptions。事件对象统一通过 utils/event/index.ts 中的emit(emitter, event)辅助函数派发emitter.emit(event.type, event)。事件对象的类型定义G6 的事件回调参数具备完整的 TypeScript 类型支持。在 types/event.ts 中IEvent是一个联合类型涵盖图生命周期事件、动画事件、元素生命周期事件、视口事件、指针事件、滚轮事件、键盘事件、拖拽事件IPointerEvent/IWheelEvent基于antv/g的FederatedEvent扩展IDragEvent额外携带dx、dy本次拖拽位移TargetedEvent将原始事件的target替换为 G6 的目标元素并补充targetType: canvas | node | edge | combo与originalTarget: DisplayObject。因此你的回调参数可以写成graph.onIPointerEvent(NodeEvent.CLICK, (evt) {...})以获得精确的类型推断。实战技巧链式调用G6 的事件 API 支持链式调用可以连续注册多个事件import { NodeEvent, EdgeEvent, CanvasEvent } from antv/g6; // 使用常量枚举 链式调用 graph.on(NodeEvent.CLICK, handleNodeClick).on(EdgeEvent.CLICK, handleEdgeClick).on(CanvasEvent.WHEEL, handleCanvasZoom);因为on、once、off都返回this你可以在一条语句中完成一组相关事件的注册或清理代码更紧凑。事件委托你可以利用事件冒泡机制在父级统一监听所有子元素的事件import { CommonEvent } from antv/g6; // 统一处理所有元素点击事件 graph.on(CommonEvent.CLICK, (evt) { const { targetType, target } evt; if (targetType node) { console.log(Clicked on node:, target.id); } else if (targetType edge) { console.log(Clicked on edge:, target.id); } else { console.log(Clicked on canvas blank area); } });这一技巧在全局点击处理场景如点击空白处取消选中中非常实用并且如前文所述它的可行性由 behavior.ts 中的双发机制直接保证。此外G6 内置的行为Behavior也大量依赖这一机制例如 brush-select.ts 通过graph.on(CommonEvent.POINTER_DOWN, ...)监听全局指针按下click-select.ts 则用${type}:${CommonEvent.CLICK}动态拼接事件名来按对象类型选择性监听——这也是理解内置行为如何与你的业务监听共存的关键线索。事件对象属性大多数事件回调函数接收的事件对象包含以下公共属性target—— 触发事件的元素targetType—— 触发事件的元素类型node / edge / combo / canvasoriginalTarget—— 触发事件的原始图形currentTarget—— 当前触发事件的对象originalEvent—— 原始浏览器事件对象借助这些属性你可以精确控制交互行为。其中target是 G6 的元素对象拥有id等数据字段而originalTarget是antv/g渲染层中实际被命中的图形如节点内部的 circle、rect 图形两者在元素内包含多个子图形的场景下有明显区别无论点击节点内部的哪一块图形target始终是该节点元素而originalTarget指向具体的子图形。测试验证生命周期事件的触发顺序仓库中的单元测试 event.spec.ts 对生命周期事件做了完整验证其中一段测试见 event.spec.ts同时监听了渲染、布局、绘制三组事件graph.render()后断言各事件各触发一次并验证触发顺序为beforeRender → beforeLayout → afterLayout → beforeDraw → afterDraw → afterRender这个顺序对业务很有参考价值如果你需要读取布局后的节点坐标应挂载在afterLayout或afterRender上如果需要在下一次绘制前拦截或修改数据可考虑beforeRender/beforeDraw。同一测试文件还验证了元素生命周期事件见 event.spec.ts初始graph.draw()时AFTER_ELEMENT_CREATE按 node-1 → node-2 → edge-1 的顺序触发 3 次且事件对象携带elementType与data随后增删改数据并再次draw()可观察到创建、更新、销毁事件分别对应正确元素而setElementState修改节点状态时不仅节点本身触发更新事件相邻边也会一并更新触发event.spec.ts。这些用例为你调试自己的事件监听提供了可复现的参考范式。小结G6 的事件系统由三部分构成以[object]:[event]为命名规范的事件体系、以on/once/off为核心的监听 API、以及覆盖节点/边/Combo/画布/生命周期/容器/全局七大类的常量枚举。实际开发中建议始终通过NodeEvent、EdgeEvent、ComboEvent、CanvasEvent、GraphEvent、ContainerEvent、CommonEvent这些枚举引用事件名并善用链式调用、事件委托与类型化的事件对象。如需更深入地学习事件在渲染、布局、动画等环节的联动可继续阅读 事件 API 文档、图 API 文档 以及仓库中丰富的 事件相关单元测试。赞分享数据可视化前端图表库【免费下载链接】G6♾ A Graph Visualization Framework in JavaScript.项目地址https://gitcode.com/gh_mirrors/g6/G6点击查看免费下载相关推荐G6 事件系统完全指南事件监听 API 与常量枚举实战G6 事件系统完全指南事件监听 API 与常量枚举实战 G6 为 JavaScript 图可视化应用提供了完整的事件机制支持响应节点点击、边悬停、画布拖拽等数据可视化前端图表库Sigma.js 事件系统完全指南从交互事件到生命周期钩子的深度解析Sigma.js 事件系统完全指南从交互事件到生命周期钩子的深度解析 Sigma.js 是一个面向大规模图可视化的 JavaScript 库其事件系统是驱动数据可视化前端图形学Archon 审批节点Approval Nodes实战指南为 AI 工作流注入人工审批闸门Archon 审批节点Approval Nodes实战指南为 AI 工作流注入人工审批闸门 导读 Archon 的 DAG 工作流支持 approval数据库ORM后端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表