☰
Vue Flow NodeToolbar 组件完全指南:为节点打造悬浮工具栏
2026/10/4 16:01:02 网站建设 项目流程
  • 前端
  • UI组件

【免费下载链接】vue-flow

A highly customizable Flowchart component for Vue 3. Features seamless zoom & pan 🔎, additional components like a Minimap 🗺 and utilities to interact with state and graph.

项目地址:https://gitcode.com/gh_mirrors/vu/vue-flow
点击查看免费下载

@vue-flow/node-toolbar是 Vue Flow(Vue 3 流程图库)官方扩展包,提供与节点绑定的浮动工具栏组件。本文从安装配置、Props 与 Slots 用法、可见性控制、定位算法到源码级实现原理,完整讲解如何在节点旁快速构建可交互的操作工具栏,并带你理解其与 Vue Flow 核心的协作机制。

包概述与定位

NodeToolbar 是 Vue Flow 生态中的官方插件之一,仓库位于packages/node-toolbar。其核心定位是:在节点旁边创建一个浮动的工具栏,用于承载删除、复制、展开等节点级操作按钮,或在用户选中节点时自动浮现操作入口。

从包结构看(src/index.ts),该包对外只导出两个东西:

  • NodeToolbar组件(即NodeToolbar.vue)
  • NodeToolbarProps等类型定义(src/types.ts)

包本身不包含 Vue Flow 核心代码,仅声明了 peer 依赖(package.json):

  • @vue-flow/core:^1.23.0
  • vue:^3.3.0

这意味着使用 NodeToolbar 的前提是项目中已安装 Vue 3 与 Vue Flow 核心。

安装与依赖

安装命令

# 使用 yarn yarn add @vue-flow/node-toolbar # 或使用 npm npm install @vue-flow/node-toolbar

@vue-flow/node-toolbar当前版本为 1.1.1(见 CHANGELOG.md)。

与核心包的版本匹配

NodeToolbar 使用 Vue Flow 核心的useVueFlow、Position枚举、NodeIdInjection(节点上下文注入)等 API,因此必须与核心包版本保持兼容。仓库的 peer 依赖声明为^1.23.0,实际使用时应确保@vue-flow/core与@vue-flow/node-toolbar版本匹配。

快速上手:把工具栏挂到自定义节点上

NodeToolbar 通常是在自定义节点的模板内部使用的,通过插槽与节点上下文自动关联到当前节点。

第一步:准备一个带自定义节点的流程图

<script setup> import { ref } from 'vue' import { VueFlow } from '@vue-flow/core' import CustomNode from './CustomNode.vue' // 节点与边数据 const nodes = ref([ { id: '1', type: 'custom', position: { x: 0, y: 0 }, data: { label: '节点 A' } }, ]) </script> <template> <VueFlow :nodes="nodes" fit-view-on-init> <template #node-custom="nodeProps"> <CustomNode :data="nodeProps.data" :label="nodeProps.label" /> </template> </VueFlow> </template>

第二步:在自定义节点内部放置 NodeToolbar

<script lang="ts" setup> import { Handle, Position } from '@vue-flow/core' import { NodeToolbar } from '@vue-flow/node-toolbar' interface NodeData { toolbarVisible: boolean toolbarPosition: Position } interface Props { data: NodeData label: string } defineProps<Props>() </script> <template> <!-- 通过 is-visible 强制显示,position 控制出现方位 --> <NodeToolbar :is-visible="data.toolbarVisible" :position="data.toolbarPosition"> <button>delete</button> <button>copy</button> <button>expand</button> </NodeToolbar> <!-- 节点本体 --> <div :style="{ padding: '10px 20px' }"> {{ label }} </div> <!-- 连接点 --> <Handle type="target" :position="Position.Left" /> <Handle type="source" :position="Position.Right" /> </template>

关键点:

  • 未显式传入nodeId时,NodeToolbar 通过 Vue 的依赖注入机制自动获取当前所在节点的 id(详见下文源码分析);
  • 工具栏内容通过默认插槽任意定制,不限于按钮;
  • 节点数据data中可以携带toolbarVisible、toolbarPosition等字段,实现每个节点各自的工具栏配置。

Props 详解

NodeToolbar 的 Props 定义在 src/types.ts,官方文档(docs/src/guide/components/node-toolbar.md)汇总如下:

名称定义类型可选默认值
nodeId工具栏要挂载到的节点(或节点数组)string \| string[]是上下文中的节点 ID
isVisible强制控制工具栏可见性boolean是节点被选中
position工具栏出现方位(top / left / right / bottom)Position是Position.Top
offset工具栏与节点的间距number是10
align工具栏沿节点边的对齐方式(center / start / end)'center' \| 'start' \| 'end'是'center'

nodeId:多节点共享一个工具栏

nodeId支持传入单个字符串或字符串数组,这是把一个工具栏同时服务多个节点的关键能力:

<NodeToolbar :node-id="['node-a', 'node-b']" position="bottom"> <button>批量操作</button> </NodeToolbar>

此时工具栏的定位基准是这些节点外接矩形的包围盒(见下方“定位算法”),适合对一组节点做批量操作。

isVisible:两种显示模式

  • 不传isVisible:默认行为是“节点被选中时自动显示工具栏”(仅当恰好选中 1 个节点且该节点就是工具栏所属节点时);
  • 传入布尔值:完全由你控制显隐,比如结合data.toolbarVisible做“常驻工具栏”或“悬停显示”。

从源码(NodeToolbar.vue)看,判定逻辑是:

const isActive = computed(() => typeof props.isVisible === 'boolean' ? props.isVisible : nodes.value.length === 1 && nodes.value[0].selected && getSelectedNodes.value.length === 1, )

即:只有当你显式传入isVisible时才以你的值为准;否则要求当前选中节点唯一且恰好是工具栏绑定的节点。1.1.1 版本将isVisible的默认值改为undefined(见 CHANGELOG.md),正是为了让“是否显式传入布尔值”这一判断可被可靠区分。

position与offset:方位与间距

position使用核心包导出的Position枚举(定义于 packages/core/src/types/flow.ts):

export enum Position { Left = 'left', Top = 'top', Right = 'right', Bottom = 'bottom', }

offset默认10,表示工具栏与节点边缘之间的像素距离。

align:沿节点边的对齐方式(1.1.0 新增)

align是 1.1.0 版本新增的 Prop(见 CHANGELOG.md),控制工具栏沿节点边(top 边或 left/right 边)的对齐位置:

  • 'center'(默认):居中对齐;
  • 'start':靠起点对齐(顶部工具栏靠左、底部靠左、左右侧工具栏靠上);
  • 'end':靠终点对齐(顶部工具栏靠右、底部靠右、左右侧工具栏靠下)。

该参数在源码中被映射为一个 0~1 的alignmentOffset参与坐标计算(见下文公式)。

Slots

NodeToolbar 只暴露一个默认插槽(docs/src/guide/components/node-toolbar.md):

名称定义
default工具栏内容,任意元素或组件

工具栏容器默认带有vue-flow__node-toolbar类名,可通过全局 CSS 定制样式(参考 docs/examples/node-toolbar/App.vue 中的示例样式:flex 布局、背景色、圆角、阴影等)。

源码级原理:工具栏如何“跟随”节点

要真正用好 NodeToolbar,理解其底层实现非常关键。核心源码在 packages/node-toolbar/src/NodeToolbar.vue。

1. 自动绑定当前节点:依赖注入

const contextNodeId = inject(NodeIdInjection, null)

当自定义节点在#node-xxx插槽中被渲染时,Vue Flow 核心会向该子树注入当前节点 id。因此在节点模板内部直接写<NodeToolbar>时,无需手动传nodeId;若在节点外部使用,则必须显式传nodeId。

2. 收集目标节点

const nodes = computed(() => { const nodeIds = Array.isArray(props.nodeId) ? props.nodeId : [props.nodeId || contextNodeId || ''] return nodeIds.reduce<GraphNode[]>((acc, id) => { const node = findNode(id) if (node) acc.push(node) return acc }, []) })

通过核心包useVueFlow()提供的findNode查找节点对象,支持单节点与多节点。

3. 包围盒计算

工具栏的定位基准是所有目标节点的外接矩形:

const nodeRect = computed(() => getRectOfNodes(nodes.value))

getRectOfNodes是核心包工具函数(packages/core/src/utils/graph.ts),它遍历节点,合并每个节点computedPosition(位置)与dimensions(尺寸),求出覆盖所有节点的最小矩形(bounding box)。

4. 分层:zIndex 自动提升

const zIndex = computed(() => Math.max(...nodes.value.map((node) => (node.computedPosition.z || 1) + 1)))

工具栏的 z-index 恒为其绑定节点的 z 值 + 1,确保工具栏永远浮于节点之上,即使多个节点重叠也不会被遮挡。

5. 定位公式

function getTransform(nodeRect, transform, position, offset, align): string { let alignmentOffset = 0.5 if (align === 'start') alignmentOffset = 0 else if (align === 'end') alignmentOffset = 1 // ...按 position 计算 pos 与 shift return `translate(${pos[0]}px, ${pos[1]}px) translate(${shift[0]}%, ${shift[1]}%)` }

以Position.Top为例:

pos = (nodeRect.x + nodeRect.width * alignmentOffset) * zoom + viewport.x , nodeRect.y * zoom + viewport.y - offset shift = [-100 * alignmentOffset, -100]
  • 第一部分translate(px):把工具栏放到节点包围盒的对应边缘,并乘以当前缩放系数zoom并加上视口平移transform.x/y,保证与画布坐标一致;
  • 第二部分translate(%):用百分比再做一次平移,实现“以节点边/中心为锚点、以工具栏自身尺寸为基准”的对齐(百分比位移基于元素自身宽度/高度),因此即使工具栏内容大小不同也能正确对齐。

该公式同时处理了Right、Bottom、Left三个方位(NodeToolbar.vue),这正是工具栏能随画布缩放平移而始终贴在节点旁的原因。

6. 渲染:Teleport 到视口容器

<Teleport :to="viewportRef" :disabled="!viewportRef"> <div v-if="isActive && nodes.length" v-bind="$attrs" :style="wrapperStyle" class="vue-flow__node-toolbar"> <slot /> </div> </Teleport>

工具栏通过Teleport挂载到核心包的视口 DOM 容器(viewportRef)中,从而与节点一样处于同一坐标变换体系之下;同时只有isActive为真且存在目标节点时才渲染。

7. 兼容性与属性透传

组件以compatConfig: { MODE: 3 }声明,兼容@vue/compat过渡模式(1.0.5 变更,见 CHANGELOG.md),并设置inheritAttrs: false以便通过v-bind="$attrs"把外部属性手动应用到工具栏容器上。

实战示例:完整可运行方案

仓库自带的演示位于 docs/examples/node-toolbar/App.vue,展示了五种典型场景:

节点配置效果
节点 1toolbarPosition: Position.Top工具栏显示在节点上方
节点 2toolbarPosition: Position.Right工具栏显示在节点右侧
节点 3toolbarPosition: Position.Bottom工具栏显示在节点下方
节点 4toolbarPosition: Position.Left工具栏显示在节点左侧
节点 5toolbarVisible: true常驻显示,不依赖选中状态

对应的节点实现(docs/examples/node-toolbar/ToolbarNode.vue)展示了两个进阶用法:

  1. 工具栏内操作节点数据:通过useVueFlow()的updateNodeData更新节点data.action,实现“选中某个表情”这类节点级状态变更:
    const { updateNodeData } = useVueFlow() // 点击按钮时: updateNodeData(props.id, { action })
  2. 结合选中态做样式反馈:利用节点自身的.selected类(示例中给.vue-flow__node-menu.selected加了蓝色描边),配合默认选中时显示工具栏的行为,形成完整的“选中即出现操作栏”交互闭环。

样式定制建议

工具栏容器类名为vue-flow__node-toolbar,官方示例(docs/examples/node-toolbar/App.vue)给出了不错的起点:

.vue-flow__node-toolbar { display: flex; gap: 0.5rem; align-items: center; background-color: #2d3748; padding: 8px; border-radius: 8px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); }

也可以直接在 NodeToolbar 上绑定 class / style,因为组件会通过$attrs把它们透传到工具栏容器上。

版本演进速览

根据 packages/node-toolbar/CHANGELOG.md,该包的关键演进如下:

  • 1.0.0:新增 node-toolbar 包(#476);
  • 1.0.1:改用重命名后的ViewportTransform类型(#553);
  • 1.0.2:等待视口 ref 可用后再进行 Teleport,避免渲染时机问题(#569);
  • 1.0.3:修正 package.json 的main字段;
  • 1.0.4:升级 Vite 4 并更新依赖(#616);
  • 1.0.5:增加 compat 模式以兼容@vue/compat(#682);
  • 1.0.6:修正 UMD 包名,使用正确的核心包 UMD 名称(#716);
  • 1.0.7:在 package.json 中新增exports字段(#865);
  • 1.1.0:新增alignProp(#953);
  • 1.1.1:isVisible默认值改为undefined(#1785)。

其中两个变更对使用者影响最大:1.1.0 的align让工具栏对齐更灵活;1.1.1 的isVisible默认值调整修正了“未显式传入时默认跟随选中态”的判断逻辑,升级后建议重新验证自定义节点的工具栏显隐行为。

小结

NodeToolbar 通过依赖注入自动绑定节点、基于包围盒与视口变换计算定位、借助 Teleport 与核心视图保持同步,并以isVisible双模式(强制显示 / 跟随选中)提供灵活的显隐控制。无论是单节点操作、多节点批量工具栏,还是“选中即弹出”的交互设计,它都能与 Vue Flow 核心无缝协作,是扩展节点交互能力的高性价比组件。

  • 前端
  • UI组件

【免费下载链接】vue-flow

A highly customizable Flowchart component for Vue 3. Features seamless zoom & pan 🔎, additional components like a Minimap 🗺 and utilities to interact with state and graph.

项目地址:https://gitcode.com/gh_mirrors/vu/vue-flow
点击查看免费下载
上一篇:IntelliJ Platform 结构搜索与替换(Structural Search and Replace)配置实战:基于 intellij-community 的 SSR 规则编写指南
下一篇:tsParticles Colored Smoke Rainbow 调色板:安装、配置与源码机制解析

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询