ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

G6 上下文菜单插件(Contextmenu)实战指南:右键菜单配置、事件回调与源码实现解析

G6 上下文菜单插件(Contextmenu)实战指南:右键菜单配置、事件回调与源码实现解析 数据可视化前端图表库【免费下载链接】G6♾ A Graph Visualization Framework in JavaScript.项目地址https://gitcode.com/gh_mirrors/g6/G6点击查看免费下载导读本文基于 G6A Graph Visualization Framework in JavaScript当前仓库的 Contextmenu.zh.md 官方文档系统讲解内置插件contextmenu的完整用法。你会掌握如何通过plugins配置快速为图元素挂载右键菜单、使用getItems/getContent两种方式生成菜单内容、通过enable精确控制菜单在节点/边/Combo 上的生效范围以及借助onClick回调拿到value、DOM 节点与当前元素实现「查看详情」「删除节点」等具体操作。文章同时结合 contextmenu 插件源码 与单元测试说明菜单定位、边界约束、事件绑定与隐藏逻辑的底层实现帮助你从「会用」进阶到「知其所以然」。概述什么是上下文菜单上下文菜单Contextmenu也被称为右键菜单是当用户在某个特定区域画布、节点、边或 Combo上点击后出现的一个菜单。它支持在点击前后触发自定义事件允许你针对某一元素进行按需的单独控制。在 G6 中contextmenu是一个内置插件通过plugins配置项注册即可使用。它适用于元素的各种交互操作场景例如查看节点详情删除节点变更边的起点对 Combo 进行展开/收起等批量操作核心价值在于把「对某一项元素的特定操作」集中到一个浮层菜单中无需占用画布空间也不依赖额外的工具栏 UI。基本用法三步挂载右键菜单在 Graph 的plugins配置中声明type: contextmenu即可。一个最简示例const graph new Graph({ plugins: [ { type: contextmenu, // 只在节点上开启右键菜单默认全部元素都开启 enable: (e) e.targetType node, getItems: () { return [{ name: 查看详情, value: detail }]; }, onClick: (value) { if (value detail) console.log(展示节点详情); }, }, ], });从源码 Contextmenu 类 可以看到插件在构造时即完成三件事initElement()通过 createPluginContainer 创建容器 DOM追加className默认g6-contextmenu并注入内置样式CONTEXTMENU_CSS到document.head绑定事件分别监听canvas、node、edge、combo四类目标的contextmenu或click事件见bindEvents调用update(options)合并默认配置。事件监听的具体实现在 bindEvents 中graph.on(canvas:${trigger}, this.onTriggerEvent); graph.on(node:${trigger}, this.onTriggerEvent); graph.on(edge:${trigger}, this.onTriggerEvent); graph.on(combo:${trigger}, this.onTriggerEvent);也就是说菜单默认在画布、节点、边、Combo 上全部生效enable配置用于按需收窄生效范围。事件类型常量可参考 节点事件枚举如NodeEvent.CONTEXT_MENU node:contextmenu。配置项详解下表为contextmenu插件的完整配置项与 ContextmenuOptions 接口一一对应属性描述类型默认值必选className给菜单的 DOM 追加的类名用于自定义样式stringg6-contextmenutrigger如何触发右键菜单contextmenu表示右键触发click表示点击触发click|contextmenucontextmenuoffset菜单显式 X、Y 方向的偏移量[number, number][4, 4]onClick当菜单被点击后触发的回调方法(value: string, target: HTMLElement, current: Element) void-getItems返回菜单的项目列表支持Promise类型的返回值是getContent的快捷配置(event: IElementEvent) Item[] | PromiseItem[]-getContent返回菜单的内容支持Promise类型的返回值也可以使用getItems进行快捷配置(event: IElementEvent) HTMLElement | string | PromiseHTMLElement | string-loadingContent当getContent返回一个Promise时使用的菜单内容加载占位HTMLElement | stringLoading...enable是否可用通过参数判断是否支持右键菜单默认是全部可用boolean | (event: IElementEvent) booleantrue源码中的默认值defaultOptions为static defaultOptions: PartialContextmenuOptions { trigger: contextmenu, offset: [4, 4], loadingContent: div classg6-contextmenu-loadingLoading.../div, getContent: () It is a empty context menu., enable: () true, };几点补充说明若未配置getItems与getContentgetContent的默认实现会返回空菜单占位文案loadingContent只在getContent返回Promise时作为加载态展示enable接收的事件类型是 IElementEvent其targetType取值为canvas | node | edge | combo见 TargetedEvent这是精确控制生效范围的关键字段。Item菜单项结构每个菜单项目Item仅含两个字段属性描述类型必选name菜单项显示的名字string✓value菜单项对应的值string✓类型定义见 util.ts 中的 Item 类型。当使用getItems时插件内部通过 getContentFromItems 将数组渲染为 HTML 字符串export function getContentFromItems(items: Item[]) { return ul classg6-contextmenu-ul ${items.map((item) li classg6-contextmenu-li value${item.value}${item.name}/li).join()} /ul ; }生成的每个li classg6-contextmenu-li节点的value属性即菜单项的value点击时会被onClick回调读取。onClick点击回调的三个参数点击菜单项后触发onClick函数有三个参数value对应菜单项的valuetarget对应菜单项容器的 DOM 节点即被点击的li元素current对应触发菜单的元素如节点/边/Combo可通过它获取元素信息如id或直接对元素进行修改。onClick的触发实现在源码 onMenuItemClick点击事件冒泡到document后判断event.target是否包含g6-contextmenu-li类名若命中则取出value属性并调用onClick?.(value, event.target, this.targetElement!)随后自动隐藏菜单如果trigger ! click任何外部点击也会隐藏菜单。代码示例基础右键菜单同时展示「查看详情」与「删除」两个菜单项trigger使用默认的右键触发const data { nodes: [ { id: node-1, type: circle, data: { cluster: node-type1 } }, { id: node-2, type: rect, data: { cluster: node-type2 } }, ], edges: [{ source: node-1, target: node-2, data: { cluster: edge-type1 } }], }; const graph new Graph({ data, layout: { type: grid }, plugins: [ { type: contextmenu, trigger: contextmenu, // click or contextmenu onClick: (value, target, current) { alert(You have clicked the「 value 」item); }, getItems: () { return [ { name: 查看详情, value: detail }, { name: 删除, value: delete }, ]; }, }, ], });注意原文档示例中回调参数v未定义实际应使用第一个参数value或onClick的参数名如上所示。边的右键菜单通过enable: (e) e.targetType edge将菜单限定在边上例如实现「变更起点」操作const data { nodes: [ { id: node-1, type: circle, data: { cluster: node-type1 } }, { id: node-2, type: rect, data: { cluster: node-type2 } }, ], edges: [{ source: node-1, target: node-2, data: { cluster: edge-type1 } }], }; const graph new Graph({ data, layout: { type: grid }, plugins: [ { type: contextmenu, trigger: contextmenu, getItems: () { return [{ name: 变更起点, value: change }]; }, onClick: (value) { if (value change) console.log(这里执行变更起点操作); }, // 仅在边上开启右键菜单 enable: (e) e.targetType edge, }, ], });异步加载菜单项getItems支持返回Promise可从服务器或其他异步源动态获取菜单配置适合菜单项需要按权限或业务动态下发的场景const data { nodes: [ { id: node-1, type: circle, data: { cluster: node-type1 } }, { id: node-2, type: rect, data: { cluster: node-type2 } }, ], edges: [{ source: node-1, target: node-2, data: { cluster: edge-type1 } }], }; const graph new Graph({ data, layout: { type: grid }, plugins: [ { type: contextmenu, trigger: contextmenu, getItems: async () { // 可以从服务器或其他异步源获取菜单配置 const response await fetch(/api/contextmenu-config); const items await response.json(); return items; }, // 仅在节点上开启右键菜单 enable: (e) e.targetType node, }, ], });异步场景下getContent同样支持PromiseHTMLElement | string此时可配合loadingContent展示加载占位。菜单内容渲染逻辑见 getDOMContentprivate async getDOMContent(event: IElementEvent) { const { getContent, getItems } this.options; if (getItems) { return getContentFromItems(await getItems(event)); } return await getContent(event); }动态控制菜单项利用getItems的回调参数event可以根据被触发元素的不同event.target、event.target.type返回不同的菜单项实现真正意义上的「按元素动态生成菜单」const data { nodes: [ { id: node-1, type: circle, data: { cluster: node-type1 } }, { id: node-2, type: rect, data: { cluster: node-type2 } }, ], edges: [{ source: node-1, target: node-2, data: { cluster: edge-type1 } }], }; const graph new Graph({ data, layout: { type: grid }, plugins: [ { type: contextmenu, trigger: contextmenu, getItems: (e) { if (e.target.id node-1) { return [ { name: 删除节点, value: delete, }, ]; } if (e.target.type edge) { return [ { name: 移动边, value: move, }, ]; } return []; }, }, ], });实际案例带交互的完整示例以下是一个可直接运行的完整案例对应仓库 plugin-contextmenu demo 的简化版构建一个以node-0为中心的星型图右键任意节点弹出「展开一度关系 / 查看详情」菜单并配合zoom-canvas、drag-canvas、drag-element三个内置交互import { Graph } from antv/g6; const data { nodes: [{ id: node-0 }, { id: node-1 }, { id: node-2 }, { id: node-3 }, { id: node-4 }, { id: node-5 }], edges: [ { source: node-0, target: node-1 }, { source: node-0, target: node-2 }, { source: node-0, target: node-3 }, { source: node-0, target: node-4 }, { source: node-1, target: node-0 }, { source: node-2, target: node-0 }, { source: node-3, target: node-0 }, { source: node-4, target: node-0 }, { source: node-5, target: node-0 }, ], }; const graph new Graph({ container: container, data, layout: { type: grid, }, behaviors: [zoom-canvas, drag-canvas, drag-element], plugins: [ { type: contextmenu, trigger: contextmenu, // click or contextmenu onClick: (v) { alert(You have clicked the「 v 」item); }, getItems: () { return [ { name: 展开一度关系, value: spread }, { name: 查看详情, value: detail }, ]; }, enable: (e) e.targetType node, }, ], }); graph.render();源码级原理剖析菜单显示与边界约束show方法是菜单出现的核心show其流程为先执行enable判断函数形式返回false或布尔值为false时直接隐藏并返回等待getDOMContent产出的内容HTMLElement 或 HTML 字符串写入菜单容器依据事件client坐标与画布容器getBoundingClientRect计算菜单位置加上offset偏移量对菜单位置做边界约束——Math.max(padding, Math.min(left, containerWidth - menuWidth - padding))确保菜单不会溢出画布容器padding 为 4px记录targetElement供onClick使用。let left event.client.x - clientRect.left offset[0]; let top event.client.y - clientRect.top offset[1]; // 限制菜单位于画布容器范围内 left Math.max(padding, Math.min(left, containerWidth - menuWidth - padding)); top Math.max(padding, Math.min(top, containerHeight - menuHeight - padding));事件绑定与默认行为处理bindEvents中监听的事件回调 onTriggerEvent 会调用event.preventDefault?.()trigger: contextmenu时阻止浏览器原生右键菜单弹出否则会出现「双层菜单」trigger: click时无需阻止默认行为。此外容器还注册了wheel事件处理器wheelHandlere.stopPropagation()阻止滚轮事件冒泡到画布避免误触画布的zoom-canvas缩放拦截逻辑见 initElement。菜单隐藏策略菜单隐藏有两个触发时机onMenuItemClick点击任意菜单项后立即隐藏当trigger ! click时点击document任意位置都会隐藏菜单document.addEventListener(click, this.onMenuItemClick)。hide()方法将容器display置为none并清空targetElement引用。单元测试 contextmenu.spec.ts 完整验证了这套行为模拟NodeEvent.CONTEXT_MENU事件后断言.g6-contextmenu-ul与两个.g6-contextmenu-li出现、点击菜单项后onClick被调用且菜单隐藏、document.body.click()后菜单再次隐藏。自定义样式插件通过 CONTEXTMENU_CSS 注入默认样式白色半透明背景、圆角、阴影、悬停高亮.g6-contextmenu-li:hover背景变灰、最大宽度 256px / 最小宽度 96px、自定义滚动条等。你可以通过className追加自定义类覆盖这些样式或直接针对.g6-contextmenu、.g6-contextmenu-li编写全局 CSS。动态更新与实例 APIcontextmenu插件遵循 G6 插件体系的通用管理 API见 runtime/plugin.ts 与 Graph 的插件方法graph.setPlugins(plugins)整体替换插件列表可传入函数基于旧配置增量修改graph.updatePlugin({ key, ... })按key更新某个插件的部分配置——注意更新前必须在插件配置中声明keygraph.getPluginInstance(key)获取插件实例从而调用show(event)、hide()、update(options)等公开方法实现「手动弹出/隐藏菜单」等高级控制graph.getPlugins()获取当前插件配置列表。例如动态切换触发方式graph.updatePlugin({ key: contextmenu, trigger: click, // 从右键触发切换为点击触发 });update方法内部会先unbindEvents()解绑旧事件再合并配置并重新bindEvents()见 update因此触发方式、菜单内容等均可运行时热更新。常见使用提示优先用getItems而非getContent前者声明式、支持Promise、自动渲染为统一风格列表适合绝大多数菜单场景只有需要完全自定义 DOM如带图标、分组、富文本的菜单时才使用getContentenable的三层能力可传布尔值全局开关、按targetType区分元素类型、或按event.target.id精确到某个具体元素注意区分targetType与target.typetargetType表示事件命中的元素类别canvas/node/edge/combotarget.type表示元素的具体类型如circle、rect菜单溢出画布插件已内置边界约束但若你的画布容器有特殊布局如被裁剪请合理设置offset或使用className定制定位不要忘记graph.render()所有plugins配置在渲染后才完整生效demo 与测试中均在render后交互。相关资源插件官方文档Contextmenu.zh.md插件核心实现contextmenu/index.ts菜单项类型与样式定义contextmenu/util.ts插件容器创建工具plugins/utils/dom.ts单元测试contextmenu.spec.ts可运行示例plugin-contextmenu.ts插件管理 APIruntime/plugin.ts、Graph 插件方法事件类型定义types/event.ts赞分享数据可视化前端图表库【免费下载链接】G6♾ A Graph Visualization Framework in JavaScript.项目地址https://gitcode.com/gh_mirrors/g6/G6点击查看免费下载相关推荐G6 Contextmenu 上下文菜单插件实战指南配置、事件机制与源码解析G6 Contextmenu 上下文菜单插件实战指南配置、事件机制与源码解析 导读本文围绕 G6 官方插件 Contextmenu上下文菜单即右键菜单数据可视化前端图表库G6 Contextmenu 右键菜单插件配置项详解与源码级实战指南G6 Contextmenu 右键菜单插件配置项详解与源码级实战指南 本文围绕 G6AntV 图可视化框架内置的 Contextmenu 插件展开系统讲数据可视化前端图表库CICFlowMeter终极指南3步构建专业级网络流量分析工具CICFlowMeter终极指南3步构建专业级网络流量分析工具 你是否曾经需要分析网络流量数据却苦于缺乏专业工具CICFlowMeter正是为解决这一痛点而上一篇【亲测免费】 Bootstrap Datepicker 安装和配置指南下一篇深入CircleImageView源码BitmapShader实现原理创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表