免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

X6 节点工具(Node Tool)实战:自定义按钮、删除按钮、包围框与文本编辑

X6 节点工具(Node Tool)实战:自定义按钮、删除按钮、包围框与文本编辑 X6 节点工具Node Tool实战自定义按钮、删除按钮、包围框与文本编辑【免费下载链接】X6 JavaScript diagramming library that uses SVG and HTML for rendering.项目地址: https://gitcode.com/GitHub_Trending/x6/X6导读本文基于 X6 官方示例 节点工具Node Tools 展开系统讲解如何在画布节点上挂载交互工具Tool包括按钮类工具自定义按钮、删除按钮、包围框Boundary和文本编辑器Node Editor并延伸到连接桩 ToolTip 的进阶玩法。读完本文你将掌握tools配置、addTools/removeTools/setTools三组 API 的使用方式理解按钮定位与包围框尺寸的底层计算逻辑并能直接复用文中的所有示例代码。一、示例页结构与运行方式该示例页属于 X6 仓库site/examples/node/下的子模块入口文档 index.en.md中文版见 index.zh.md本身仅声明页面标题与路由重定向真正的演示内容由 demo 目录 下的五个可运行示例承载示例清单定义在 meta.json示例文件主题说明button.tsCustom Button 自定义按钮按钮点击后随机改变节点填充色button-remove.tsDelete Button 删除按钮一键移除节点boundary.tsBoundary Box 包围框高亮选中范围editable.tsText Editor 文本编辑双击节点/边直接编辑文本port-tooltip.tsxToolTip For Port 连接桩提示悬停连接桩显示提示气泡运行这些示例无需额外配置示例均在new Graph({ container, grid: true })基础上直接调用节点 API 完成可复制到任意 X6 项目中验证。二、节点工具的挂载机制tools、addTools、removeTools所有示例共用一套工具挂载机制。工具既可以随节点创建时静态声明写入节点配置的tools数组也可以在运行时通过 Cell 实例方法 动态增删node.addTools(tools, options)向节点追加一组工具node.setTools(tools, options)整体替换工具集合node.removeTools(options)清空当前节点的所有工具。示例中典型的写法如下静态声明与动态挂载两处配置完全一致// 静态声明节点创建时即携带工具 const source graph.addNode({ x: 180, y: 60, width: 100, height: 40, attrs: { body: { fill: #f5f5f5, stroke: #d9d9d9, strokeWidth: 1 } }, tools: [{ name: button, args: { /* ... */ } }], }) // 动态挂载鼠标进入时添加工具离开时移除 graph.on(node:mouseenter, ({ node }) { if (node target) node.addTools({ name: button, args: { /* ... */ } }) }) graph.on(node:mouseleave, ({ cell }) { if (cell target) cell.removeTools() })工具项Tool Item本身是独立于节点可视区域之外渲染的 SVG/DOM 元素由src/view/tool/下的 ToolItem 基类 与 ToolView 容器 统一管理通过args传入的x、y、offset、onClick等参数控制其表现与行为。三、内置节点工具一览X6 将工具以注册表Registry形式组织节点工具的全部内置项定义在 src/registry/tool/index.ts 的nodeToolPresets中export const nodeToolPresets { boundary: Boundary, button: Button, button-remove: Remove, node-editor: NodeEditor, }即节点上内置四类原生工具boundary包围框、button自定义按钮、button-remove删除按钮、node-editor文本编辑器。其中button-remove与node-editor分别是Button与编辑器基类的派生/组合实现后面逐一展开。工具名可直接写在tools数组的name字段配合args传参无需手动register。四、自定义按钮button4.1 完整示例button.ts 演示了两种挂载形态源节点在创建时通过tools静态携带按钮目标节点在node:mouseenter时动态追加按钮、node:mouseleave时移除。其核心配置如下tools: [ { name: button, args: { markup: [ { tagName: circle, selector: button, attrs: { r: 14, stroke: #fe854f, strokeWidth: 2, fill: white, cursor: pointer, }, }, { tagName: text, textContent: Btn, selector: icon, attrs: { fill: #fe854f, fontSize: 10, textAnchor: middle, pointerEvents: none, y: 0.3em, }, }, ], x: 100%, y: 100%, offset: { x: -20, y: -20 }, onClick({ cell }: { cell: Cell }) { const fill Color.randomHex() cell.attr({ body: { fill }, label: { fill: Color.invert(fill, true) }, }) }, }, }, ],点击按钮后调用cell.attr(...)修改节点的body与label填充色实现一键换肤。动态挂载版本额外展示了按钮点击改变描边样式的效果随机描边色 strokeDasharray/strokeDashoffset递增的虚线动画效果。4.2 参数详解对应源码 src/registry/tool/button.ts参数类型说明markupMarkup数组按钮的自定义 SVG 结构可包含多个元素并按selector命名x/ynumber \| string按钮锚点在节点包围盒内的位置支持百分比字符串如100%定位到右下角底层通过NumberExt.normalizePercentage(x, bbox.width)归一化offsetnumber \| { x, y }锚点基础上的额外偏移传数字时 x、y 同时生效rotateboolean是否跟随节点旋转结合cell.getAngle()处理useCellGeometryboolean默认true使用节点几何数据计算包围盒onClick(args) void点击回调回调参数含{ e, cell, view, btn }其中btn为按钮工具实例按钮的定位逻辑在getNodeMatrix()中先取节点包围盒必要时按角度旋转再以包围盒中心为原点将归一化后的x/y与offset组合成 SVG 变换矩阵Dom.transform(...)应用到按钮容器上。onClick在基类的onMouseDown中触发事件绑定了mousedown与touchstart因此同时支持鼠标与触屏操作。五、删除按钮button-remove5.1 使用方式button-remove.ts 用法与button完全一致只需把name换成button-removetools: [ { name: button-remove, args: { x: 100%, y: 0, offset: { x: -10, y: 10 }, }, }, ],它默认渲染为一个红色圆形#FF1D00内嵌白色“×”图标的删除按钮点击后节点连同按钮一起从画布移除。5.2 默认配置与删除逻辑源码 src/registry/tool/button.tsRemove继承自Button其默认markup为圆形 两条交叉线段组成的删除图标默认点击行为是onClick({ view, btn }) { btn.parent.remove() // 移除按钮本身 view.cell.remove({ ui: true, toolId: btn.cid }) // 删除节点 }即先移除按钮所在工具组ToolView再以{ ui: true }语义删除对应节点避免触发 UI 无关的回调toolId用于在删除时精确跳过该工具自身。其余定位参数x、y、offset、distance等与button通用。六、包围框boundary6.1 使用方式boundary.ts 用包围框高亮节点静态挂载与动态挂载并存tools: [ { name: boundary, args: { padding: 5, attrs: { fill: #7c68fc, stroke: #9254de, strokeWidth: 1, fillOpacity: 0.2, }, }, }, ],效果是节点四周出现一圈带透明填充、紫色描边的矩形框。动态版本在鼠标悬停时node.addTools({ name: boundary, args: { attrs: { ... } } })移出时node.removeTools()。6.2 参数与渲染原理源码 src/registry/tool/boundary.ts参数类型默认值说明paddingnumber \| SideOptions10包围框与节点边缘的间距支持{ top, right, bottom, left }分别设置useCellGeometrybooleantrue是否基于节点几何数据计算包围盒rotateboolean—为true时包围框随节点旋转否则按旋转后的轴对齐包围盒bbox.bbox(angle)attrsSimpleAttrsfill: none、stroke: #333、stroke-width: 0.5、stroke-dasharray: 5, 5、pointer-events: none包围框 rect 的样式属性渲染时工具会通过Util.getViewBBox(view, useCellGeometry)取得节点包围盒用NumberExt.normalizeSides(padding)归一化 padding 并调用moveAndExpand外扩矩形若节点有旋转角度依据rotate选择“随动旋转”或“轴对齐重算”将最终bbox写入容器 rect 的x/y/width/height属性。默认样式是虚线描边且pointer-events: none因此不会拦截鼠标事件适合做纯视觉的高亮反馈。七、文本编辑node-editor / edge-editor7.1 使用方式editable.ts 展示了节点与边上的文本编辑器// 节点 tools: [ { name: node-editor, args: { attrs: { backgroundColor: #EFF4FF }, }, }, ], // 边同理 tools: [ { name: edge-editor, args: { attrs: { backgroundColor: #fff }, }, }, ],挂载后双击节点或边即可在其上直接输入文本contentEditable模式编辑结果写回模型。7.2 底层实现源码 src/registry/tool/editor.tsNodeEditor/EdgeEditor由编辑器基类CellEditor派生。关键设计点触发方式工具渲染时监听cell:dblclickonRender中绑定双击进入编辑态编辑介质创建一个contentEditable true的 HTMLdiv覆盖在节点/边上样式fontSize、fontFamily、color、backgroundColor取自args.attrs定位逻辑未指定x/y时编辑器居中于节点包围盒translate(-50%, -50%)并经过graph.localToGraph将局部坐标换算为画布坐标兼容缩放场景graph.scale()交互边界绑定mousedown/touchstart与文档级mouseup/touchend/touchcancel保证编辑结束、失焦行为的一致处理。相关行为在 editor 测试 中有覆盖可作为自定义编辑器行为的参考。八、连接桩 ToolTipport-tooltip最后一个示例 port-tooltip.tsx 不是内置工具而是“回调 第三方 Tooltip 组件”组合出的交互利用Graph的onPortRendered回调在连接桩 DOM 渲染完成后为每个桩注册鼠标事件再结合 antd 的Tooltip显示提示文本const graph new Graph({ container: this.container, width: 800, height: 600, grid: true, onPortRendered({ contentContainer, port, node }) { const text node.portProp(port.id, tip) as string registerPortTooltip(contentContainer, text) }, }) // 连接桩上自定义 tip 属性 ports: { groups: { bottom: { position: bottom, attrs: { circle: { magnet: true } } } }, items: [{ id: port-1, group: bottom, tip: port-1-tip }], }registerPortTooltip在mouseenter时把portProp(port.id, tip)读到的文本写入ant-tooltip-inner并将气泡定位到鼠标附近mouseleave时把气泡移到屏幕外-1000px。用 React 渲染示例时Tooltip组件常驻页面并设置overlayClassNamex6-tooltip作为移动载体。该模式也可迁移到任意 UI 组件库仅需将Tooltip换成对应实现。九、常见问题与最佳实践何时用静态tools数组何时用addTools常驻型工具如始终显示的删除按钮、包围框适合写在节点配置里交互型工具悬停才出现应放在node:mouseenter/node:mouseleave事件中动态增删注意用节点引用做判断避免所有节点共享同一套工具。removeTools()会清空全部工具如果只想移除单个工具需自行管理工具实例或使用setTools整体重设。按钮遮挡交互自定义markup时图标元素建议设置pointerEvents: none示例中的text元素即如此让点击事件只落在按钮容器上。包围框拦截事件若包围框影响拖拽保持默认的pointer-events: none即可需要可交互的框再覆盖attrs。百分比定位依赖包围盒x: 100%、y: 100%按节点宽高归一化offset再做像素级微调二者组合可精确控制按钮落在节点边角内侧或外侧。十、结语节点工具是 X6 交互体系中最常用的一环button与button-remove解决“点一下做什么”的入口问题boundary提供轻量高亮node-editor让文本就地可编辑而onPortRendered Tooltip 的组合则展示了向连接桩扩展交互的通用思路。结合 节点工具注册表 与各工具源码button.ts、boundary.ts、editor.ts你既可以直接复用官方示例也可以基于ToolItem基类派生自己的业务工具。【免费下载链接】X6 JavaScript diagramming library that uses SVG and HTML for rendering.项目地址: https://gitcode.com/GitHub_Trending/x6/X6创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表