- 前端
- UI组件
- 图表库
【免费下载链接】xyflow
React Flow | Svelte Flow - Powerful open source libraries for building node-based UIs with React (https://reactflow.dev) or Svelte (https://svelteflow.dev). Ready out-of-the-box and infinitely customizable.
@xyflow/system是 xyflow 仓库中驱动 React Flow 与 Svelte Flow 的共享工具层,封装了边路径计算、平移缩放、节点拖拽、连接线、缩略图等全部底层逻辑。本文将结合 packages/system/README.md 与其源码实现,系统讲解该包的定位、功能清单、安装方式与核心 API,帮助你理解两个框架"开箱即用"能力背后的统一实现,并掌握在纯 TypeScript 场景下复用这些工具的方法。
什么是 @xyflow/system
在 xyflow 单仓库中,packages/react 与 packages/svelte 是两个独立的框架适配层,而@xyflow/system位于两者之下,提供一套**框架无关(framework-agnostic)**的 vanilla TypeScript 工具与类型。正如其 README 所言:"Core system utilities powering React Flow and Svelte Flow",它包含两个框架之间共享的逻辑与工具函数,例如边路径计算(edge path calculations)、平移缩放(pan/zoom)、节点拖拽(node dragging)等。
该包的设计目标是作为 React Flow 和 Svelte Flow 的共享基础层,不面向无关库使用(README 明确注明 "not intended for use with unrelated libraries")。从依赖声明看,它直接依赖d3-drag、d3-interpolate、d3-selection、d3-zoom等 D3 交互原语,在此基础上封装出面向流程图编辑场景的高层能力。
从源码结构看,包的入口 packages/system/src/index.ts 仅做统一转发:
export * from './constants'; export * from './types'; export * from './utils'; export * from './xydrag'; export * from './xyhandle'; export * from './xyminimap'; export * from './xypanzoom'; export * from './xyresizer';可见包内共有三大类内容:constants(常量)、types(类型)、utils(工具函数),以及四个以xy前缀命名的交互模块(拖拽、Handle、缩略图、平移缩放、尺寸调整)。
安装与构建信息
在 xyflow monorepo 中,@xyflow/system通过 pnpm workspace 管理,React 与 Svelte 两个包均以"@xyflow/system": "workspace:*"引用(见 packages/react/package.json 与 packages/svelte/package.json)。
若要在自己的项目中使用,按 README 说明执行:
pnpm add @xyflow/system从 packages/system/package.json 可以了解到该包的工程细节:
- 版本:
0.0.82,MIT 许可,sideEffects: false(可安全参与 tree-shaking); - 发布形态:同时提供 UMD(
dist/umd/index.js)、ESM(dist/esm/index.js与index.mjs)与类型声明(dist/esm/index.d.ts),Node 与浏览器环境均有对应的导出条件; - 构建方式:基于
@xyflow/rollup-config的 rollup 配置,rollup.vanilla = true,全局名为XYFlowSystem,并将d3-selection、d3-zoom、d3-drag声明为 external globals,因此打包产物不含 D3 代码; - 质量保障:提供
dev、build、lint、typecheck脚本,其中typecheck通过tsc --noEmit对全量源码做类型检查。
功能全景:README 中的 11 项能力
README 的 Features 清单概括了该包的全部能力,结合源码逐项对应如下:
| 功能 | 说明 | 源码位置 |
|---|---|---|
Pan & Zoom(XYPanZoom) | 画布平移与缩放交互 | xypanzoom/XYPanZoom.ts |
Dragging(XYDrag) | 节点与多选集合的拖拽 | xydrag/XYDrag.ts |
Handles/Connections(XYHandle) | 节点连接点与连线管理 | xyhandle |
Minimap(XYMiniMap) | 缩略图总览与导航 | xyminimap |
| Edge Utilities | SVG 边路径生成(bezier、straight、step、smoothstep 等) | utils/edges |
| Store Utilities | 流程状态管理与更新 | utils/store.ts |
| DOM Utilities | DOM 测量与交互辅助 | utils/dom.ts |
| Marker Utilities | 边的 SVG marker 处理 | utils/marker.ts |
| Graph Utilities | 节点、边与图结构运算 | utils/graph.ts |
| General Utilities | 通用杂项工具 | utils/general.ts |
| Types & Constants | 共享类型、枚举与常量 | types 与 constants.ts |
工具的聚合入口为 utils/index.ts,它再导出 attribution、connections、dom、edges、graph、general、marker、node-toolbar、edge-toolbar、store、types、shallow-node-data 等模块;而边的工具又细分出 bezier-edge.ts、straight-edge.ts、smoothstep-edge.ts、general.ts 与 positions.ts。
入门用法:直接导入工具函数
README 强调所有工具、类型、helper 都可直接从包中导入:
import { getBezierPath, getConnectedEdges, Position, XYPanZoom } from '@xyflow/system';下面的示例演示了最常用的边路径计算函数getBezierPath:
import { getBezierPath, Position } from '@xyflow/system'; const [path, labelX, labelY] = getBezierPath({ sourceX: 0, sourceY: 20, sourcePosition: Position.Right, targetX: 150, targetY: 100, targetPosition: Position.Left, });返回值是一个元组(固定长度数组),这在源码的 JSDoc 中有明确说明——"This function returns a tuple to make it easier to work with multiple edge paths at once"。五个返回项依次为:可直接用于 SVG<path>元素的path字符串、路径中心的labelX/labelY(用于渲染边标签)、以及源 Handle 到路径中点之间的绝对偏移offsetX/offsetY。
边路径计算:从参数到 SVG 的完整链路
getBezierPath:三次贝塞尔曲线
bezier-edge.ts 定义了getBezierPath的完整参数与默认值:
| 参数 | 说明 | 默认值 |
|---|---|---|
sourceX/sourceY | 源 Handle 的 x/y 坐标 | 必填 |
sourcePosition | 源 Handle 所在方位 | Position.Bottom |
targetX/targetY | 目标 Handle 的 x/y 坐标 | 必填 |
targetPosition | 目标 Handle 所在方位 | Position.Top |
curvature | 曲线弯曲程度 | 0.25 |
其内部实现分三步(可追溯源码):
getControlWithCurvature依据 Handle 方位(Left/Right/Top/Bottom)计算源端与目标端的控制点;当两点距离较大时,控制点偏移为0.5 * distance,距离较小时则退化为curvature * 25 * Math.sqrt(-distance)的非线性收缩(calculateControlOffset),从而保证短边不会出现夸张的曲线;getBezierEdgeCenter用三次贝塞尔在t = 0.5处的公式source * 0.125 + control * 0.375 + ...估算路径中心点与偏移量(该处注释注明这不是弧长意义上的真正中点,而是一个易计算的近似中点);- 最终拼出 SVG path 指令:
M${sourceX},${sourceY} C${sourceControlX},${sourceControlY} ${targetControlX},${targetControlY} ${targetX},${targetY}。
getStraightPath 与 getSmoothStepPath
直线边 straight-edge.ts 的getStraightPath只接受源/目标的坐标,内部通过getEdgeCenter计算中点,直接输出M ${sourceX},${sourceY}L ${targetX},${targetY}。
阶梯边 smoothstep-edge.ts 的getSmoothStepPath则参数更丰富:
borderRadius(默认5):阶梯拐角的圆角半径,0即得到直角 Step 边;offset(默认20):边从 Handle 出发后沿方位方向延伸的距离,相当于正交布线中的"出线长度";stepPosition(默认0.5):折弯点沿主方向的相对位置,0表示折弯发生在源端、1表示在目标端、0.5为中点;centerX/centerY:可手动覆盖折弯中心。
源码注释说明,getPoints的设计目标是"模拟正交边路由(orthogonal edge routing)"——虽然不如真正的正交路由引擎精细,但更快,作为 Step/SmoothStep 边的默认实现足够好。它内部会处理多种特殊情况:相反方位 Handle 的默认路径、同方位 Handle 之间距离小于offset时自动追加gapOffset防止折弯重叠、混合方位(如 Right → Bottom)时的flipSourceTarget翻转逻辑等;标签位置则被放在路径中最长的线段上。生成的路径由getBend用L/Q指令逐段拼接圆角。
边的增删与 ID 生成
utils/edges/general.ts 还提供了边管理相关的实用函数:
getEdgeId:默认边 ID 生成器,格式为xy-edge__${source}${sourceHandle || ''}-${target}${targetHandle || ''},与 React Flow / Svelte Flow 渲染出的边 DOM id 一致;addEdge(edgeParams, edges, options):向边数组追加一条边,同时做两类校验——缺少source/target时报错error006;若已存在 source/target/handle 完全相同的边则直接返回原数组(即使id不同也不会重复添加)。options.getEdgeId可自定义 ID 生成,options.onError可接管错误处理;reconnectEdge(oldEdge, newConnection, edges, options):按id找到旧边,用新连接替换其 source/target/handle 属性;shouldReplaceId(默认true)决定是否用新连接的 ID 替换旧 ID,找不到旧边时触发error007;getElevatedEdgeZIndex:根据连接节点与选中状态计算边层级。默认情况下边渲染在节点之下,但连接到带父级节点的边会渲染在父节点之上;zIndexMode: 'manual'时完全返回手动zIndex,选中提升(elevateOnSelect)时选中边在zIndex基础上加1000;isEdgeVisible:用源/目标节点的包围盒与当前视口(由 transform 反推)做重叠检测,用于渲染时的可见性裁剪。
从 Handle 到路径坐标:getEdgePosition
utils/edges/positions.ts 的getEdgePosition把"哪条边、连接哪个 Handle"解析成getBezierPath所需的源/目标坐标与方位。它会校验源/目标节点是否已初始化(有 handleBounds 且测得宽高),从handleBounds.source/handleBounds.target中按handleId查找 Handle(未指定时取第一个),并依据ConnectionMode决定目标端可连接的 Handle 集合(Strict模式只允许 source→target,宽松模式允许 source→source)。找不到 Handle 时触发error008。getHandlePosition则根据 Handle 的方位(Top/Right/Bottom/Left)与节点绝对坐标计算出锚点位置。
画布交互模块:XYPanZoom、XYDrag、XYMiniMap、XYResizer
XYPanZoom:基于 d3-zoom 的平移缩放
xypanzoom/XYPanZoom.ts 导出的XYPanZoom工厂函数接收domNode、minZoom、maxZoom、translateExtent、viewport以及onPanZoom/onPanZoomStart/onPanZoomEnd/onDraggingChange回调,返回一个PanZoomInstance(提供scaleTo、setViewport等方法,缩略图模块即通过panZoom.scaleTo(nextZoom)驱动主画布缩放)。
源码中有两个值得注意的实现细节:
- 通过
ResizeObserver持续缓存 pane 的 extent(cachedExtent),避免 d3-zoom 在拖拽/捏合过程中回退到默认 extent 而触发同步布局;该 observer 故意不主动断开,因为destroy()还会在用户框选时暂停缩放,断开会导致缓存失效,最终随 pane 卸载被 GC 回收; - 事件处理被拆分到 eventhandler.ts(创建 pan/zoom 的 start/change/end 处理器)与 filter.ts(事件过滤)中,滚动缩放、平移滚动分别由
createZoomOnScrollHandler与createPanOnScrollHandler实现。
XYDrag:节点与选区拖拽
xydrag/XYDrag.ts 基于d3-drag封装节点拖拽逻辑。其OnDrag回调签名为(event, dragItems, node, nodes),拖拽过程中会综合处理节点 extent 约束、父节点位置、snapToGrid吸附网格、自动平移(autoPanOnNodeDrag)、多选拖拽与 touch 事件(OnNodeDrag同时接受MouseEvent | TouchEvent)。拖拽所需的指针坐标换算、节点位置计算与吸附逻辑分别来自 utils/dom.ts 与 utils/general.ts。
XYMiniMap:缩略图交互
xyminimap/index.ts 导出的XYMiniMap({ domNode, panZoom, getTransform, getViewScale })返回{ update, destroy, pointer }实例。update接受translateExtent、width、height、zoomStep(默认 1)、pannable(默认 true)、zoomable(默认 true)、inversePan等参数;滚轮事件通过panZoom.scaleTo(nextZoom)联动主画布缩放,macOS 上按住 Ctrl 滚轮会放大 10 倍增量。React Flow / Svelte Flow 的 MiniMap 插件组件本质上就是这个实例的薄封装。
XYResizer:节点尺寸调整
xyresizer/XYResizer.ts 是 NodeResizer 的底层实现,基于d3-drag提供八个方向(由ControlPosition/ResizeControlDirection定义)的拖拽缩放,支持minWidth/minHeight/maxWidth/maxHeight边界、keepAspectRatio保持宽高比、网格吸附,以及通过onChange/onEnd回调把XYResizerChange(x/y/width/height 的增量)与子节点位置变化(XYResizerChildChange)反馈给上层。
图结构与状态工具
节点/边关系查询
utils/graph.ts 集中了图算法相关工具:
isEdgeBase/isNodeBase/isInternalNodeBase:TypeScript 类型守卫,分别通过"含 id、source、target"、"含 id、position 且无 source/target"、"含 id、internals 且无 source/target"来判别对象类型,是解析用户数据时的重要基础;getOutgoers(node, nodes, edges)/getIncomers(node, nodes, edges):返回以给定节点为源(source)出发、或以该节点为目标(target)汇入的相邻节点集合;getConnectedEdges(nodes, edges):返回端点出现在给定节点集合中的全部边;getNodesBounds(nodes, params):计算包含所有给定节点的包围盒(Rect),nodeOrigin参数支持[0, 0](左上)与[0.5, 0.5](中心)等原点配置;开发环境下不带nodeLookup调用会打印警告,提示子流程场景请通过useReactFlow/useSvelteFlow获取正确的值;getNodesInside:按视口矩形与 transform 反推流坐标系中的可见节点,支持partially部分可见与excludeNonSelectableNodes过滤,是"只渲染可视区域节点"性能优化(虚拟化)的依据;fitViewport:计算恰好容纳所有节点的 transform 并调用panZoom.setViewport执行动画(duration、ease、interpolate可选),即fitView功能的底层实现;calculateNodePosition:综合节点 extent、父节点与 origin 计算节点的下一个位置(返回position与positionAbsolute)。
DOM 与通用工具
utils/dom.ts 提供getPointerPosition(把鼠标/touch 事件换算为渲染器坐标,可选 snap 网格吸附)、getDimensions(读取 offsetWidth/offsetHeight)、isInputDOMNode(判断事件目标是否为输入框/可编辑元素或.nokey元素,用于避免在输入时误触删除/移动快捷键)等。getEventPosition同时兼容鼠标与触摸事件(touches[0])。
utils/general.ts 则包含clamp、clampPosition(把节点位置约束进CoordinateExtent)、clampPositionToParent(子节点约束在父节点范围内)、calcAutoPanVelocity(鼠标接近画布边缘时的自动平移速度计算,返回值在 0~1 之间)等基础函数。
Marker 工具
utils/marker.ts 负责边箭头的 SVG marker 管理:getMarkerId把 marker 配置序列化为唯一 id(字符串形式的 marker 直接返回自身,对象形式则按 key 排序拼接id__key=value&...);createMarkerIds遍历边的markerStart/markerEnd(或全局默认值),去重后生成 Marker 定义列表供<defs>渲染,并按 id 排序保证稳定性。
Store 工具与常量
utils/store.ts 是两框架状态管理的中枢:updateAbsolutePositions遍历节点表,对无父节点者按 origin 计算绝对位置并做 extent 约束,对有父节点者调用updateChildNode递归联动;parseHandles把用户声明的 handles 解析为内部NodeHandleBounds(source/target 分组,宽高缺省为 1)。常量方面,constants.ts 定义了infiniteExtent(无限 extent)、elementSelectionKeys(Enter/空格/Escape)、defaultAriaLabelConfig(节点、边、控件、缩略图的默认无障碍文案,供 A11y 功能使用)以及从error001到error016的全量错误消息模板——例如error013提示未加载dist/style.css,error001提示缺少ReactFlowProvider/SvelteFlowProvider,error015提示拖拽了未初始化的节点。
它如何支撑 React Flow 与 Svelte Flow
从依赖关系可以确认@xyflow/system的实际消费方式:React Flow 与 Svelte Flow 均将其作为 workspace 依赖引入,各自的组件层(如EdgeWrapper、NodeWrapper、FlowRenderer、各类插件)直接调用上述工具函数完成渲染与交互,而框架层只负责 React/Svelte 的响应式状态与 DOM 挂载。这正是 xyflow 能在两个框架间保持行为一致(同样的边路径、同样的拖拽手感、同样的缩放与 fitView 逻辑)的原因。
需要留意的是:README 明确指出该包"不面向无关库使用",且当前没有专门的 API 文档("There is currently no dedicated API documentation"),官方推荐直接阅读 packages/system/src 源码,或查阅 React Flow 文档的 API Reference 章节——其中大量内容正是从本包导出的。因此,若要深度定制 React Flow / Svelte Flow 的行为(例如编写自定义边、自定义拖拽逻辑或复用路径计算),本包源码就是最权威的参考资料。
小结
@xyflow/system是 xyflow 生态的"引擎舱":它用一套与框架解耦的 TypeScript 实现,统一承载了节点编辑器所需的最底层能力——从单条贝塞尔边的坐标计算,到整图的可视区域裁剪、拖拽、缩放、缩略图与 fitView。理解它的模块划分(constants / types / utils / xy* 交互模块)、核心 API 签名与默认值,既能帮助你更精准地排查 React Flow / Svelte Flow 使用中的疑难问题,也为基于这两大框架构建自定义节点编辑器提供了坚实的技术底座。
- 前端
- UI组件
- 图表库
【免费下载链接】xyflow
React Flow | Svelte Flow - Powerful open source libraries for building node-based UIs with React (https://reactflow.dev) or Svelte (https://svelteflow.dev). Ready out-of-the-box and infinitely customizable.
相关推荐
深入Prompt Flow核心概念:Flow、Tool与Prompty详解
深入Prompt Flow核心概念:Flow、Tool与Prompty详解 本文深入解析了Prompt Flow生态系统的三大核心概念:Flow、Tool和Pr
ReMe记忆检索完全指南:BM25+向量混合搜索实战教程
ReMe记忆检索完全指南:BM25+向量混合搜索实战教程 ReMe 是一款面向 AI Agent 的记忆管理工具包(Remember Me, Refine Me
人工智能Agent 记忆知识库RAGMCP 服务Source Serif 4 开源衬线字体完整指南:字重体系、光学尺寸与网页集成一次讲透
Source Serif 4 开源衬线字体完整指南:字重体系、光学尺寸与网页集成一次讲透 如果你是排版设计师或前端开发者,大概率经历过这样的场景:正文小字一缩到
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考