X6 连接器(Connector)完全指南:内置连接器使用、自定义注册与 SVG 路径生成原理
【免费下载链接】X6🚀 JavaScript diagramming library that uses SVG and HTML for rendering.项目地址: https://gitcode.com/GitHub_Trending/x6/X6
导读
在 X6 中,连接器(Connector)负责把边的起点、路由(Router)返回的中间点与终点加工为 SVG<path>元素的d属性,是决定边渲染形态(直线、贝塞尔曲线、圆角、跳线等)的核心一环。本文以官方文档 connector.zh.md 为骨架,结合仓库源码(src/registry/connector)与模型层实现,完整讲解内置连接器的参数用法、全局/局部配置方式、自定义连接器的函数签名与注册流程,并深入剖析每种内置连接器的底层几何实现,帮助你在实际项目中精确控制边的视觉表现。
什么是连接器
连接器在 X6 的渲染管线中扮演"最后成型"的角色:路由(Router)负责计算折线经过的拐点,连接器则将这些离散的点位转换成可供 SVG 渲染的路径数据。在 src/registry/connector/index.ts 中,连接器的类型定义如下:
export type ConnectorDefinition< T extends ConnectorBaseOptions = ConnectorBaseOptions, > = ( this: EdgeView, sourcePoint: PointLike, targetPoint: PointLike, routePoints: PointLike[], options: T, edgeView: EdgeView, ) => Path | string其核心语义是:输入sourcePoint(起点)、targetPoint(终点)、routePoints(路由返回的点),输出一个几何Path对象或序列化后的路径字符串。连接器的返回值最终会写入<path>元素的d属性,从而决定边渲染到画布后的样式。
X6 内置了以下几种连接器:
| 连接器 | 说明 |
|---|---|
| normal | 简单连接器,用直线连接起点、路由点和终点。 |
| smooth | 平滑连接器,用三次贝塞尔曲线连接起点、路由点和终点。 |
| rounded | 圆角连接器,用直线连接起点、路由点和终点,并在线段连接处用圆弧链接(倒圆角)。 |
| jumpover | 跳线连接器,用直线连接起点、路由点和终点,并在边与边的交叉处用跳线符号链接。 |
连接器的三种配置方式
1. 在创建边时指定
const edge = graph.addEdge({ source, target, connector: { name: 'rounded', args: { radius: 20, }, }, })当没有连接器参数时,可以简化为:
const edge = graph.addEdge({ source, target, connector: 'rounded', })2. 通过边实例方法动态设置
edge.setConnector('rounded', { radius: 20 })setConnector定义于 src/model/edge.ts,支持两种重载形式:既可以传(name, args),也可以直接传完整的ConnectorData对象;对应的getConnector()方法用于读取当前边的连接器配置。与之配套的还有removeConnector(),用于移除连接器设置。
3. 创建画布时设置全局默认
new Graph({ connecting: { connector: { name: 'rounded', args: { radius: 20, }, }, }, })同样可以简化为:
new Graph({ connecting: { connector: 'rounded', }, })全局默认连接器为'normal',即所有未单独指定连接器的边默认以直线连接。
提示:从源码结构看(src/registry/connector/jumpover.ts),跳线连接器还会读取
graph.options.connecting.connector作为回退的默认连接器配置,说明connecting.connector同时承担"默认外观"与"交叉检测过滤依据"两个职责。
内置连接器详解
normal —— 简单直线连接器
系统的默认连接器,将起点、路由点、终点通过直线按顺序连接。其实现位于 src/registry/connector/normal.ts,核心逻辑是把所有点组成一个Polyline再转换为Path:
const points = [sourcePoint, ...routePoints, targetPoint] const polyline = new Polyline(points) const path = new Path(polyline) return options.raw ? path : path.serialize()支持的参数如下表:
| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 |
|---|---|---|---|---|
| raw | boolean | 否 | false | 是否返回一个Path对象,默认值为false返回序列化后的字符串。 |
补充说明:在源码的NormalConnectorOptions(normal.ts)中还声明了split?: boolean | number选项,用于控制路径分段行为,具体效果以实际版本为准。raw是全部内置连接器共有的基础选项(定义于 index.ts 的ConnectorBaseOptions),当需要直接拿到几何对象做二次加工(如旋转、裁剪)时非常有用。
smooth —— 平滑连接器
平滑连接器通过三次贝塞尔曲线连接起点、路由点和终点。实现位于 src/registry/connector/smooth.ts,其内部逻辑分为两条路径:
- 存在路由点时,使用
Curve.throughPoints(points)生成贯穿所有点的光滑曲线; - 没有路由点时,退化为一条默认的三次贝塞尔曲线,两个控制点的
x坐标位于起点与终点的中点位置,形成经典的 S 形曲线。
支持的参数如下表:
| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 |
|---|---|---|---|---|
| raw | boolean | 否 | false | 是否返回一个Path对象,默认值为false返回序列化后的字符串。 |
| direction | H|V | 否 | - | 保持水平连接或者保持垂直连接,不设置会根据起点和终点位置动态计算。 |
direction的动态判定逻辑(smooth.ts):当起点与终点的水平距离不小于垂直距离时自动选择'H',否则选择'V';手动指定后,控制点会分别固定在水平/垂直方向的中点上,从而得到更"顺滑直出"的 S 形曲线。
示例:
graph.addEdge({ source: rect1, target: rect2, vertices: [ { x: 100, y: 200 }, { x: 300, y: 120 }, ], connector: 'smooth', })rounded —— 圆角连接器
圆角连接器将起点、路由点、终点通过直线按顺序连接,并在线段连接处通过圆弧连接(倒圆角)。实现位于 src/registry/connector/rounded.ts,其关键技巧是:在每个路由点处先用Math.min(radius, prevDistance)与Math.min(radius, nextDistance)计算实际倒角半径(避免半径超过相邻线段长度的一半),然后通过L直线段接近拐点、再用C三次贝塞尔曲线平滑过渡:
const startMove = -Math.min(radius, prevDistance) const endMove = -Math.min(radius, nextDistance) const roundedStart = curr.clone().move(prev, startMove).round() const roundedEnd = curr.clone().move(next, endMove).round() path.appendSegment(Path.createSegment('L', roundedStart)) path.appendSegment(Path.createSegment('C', control1, control2, roundedEnd))支持的参数如下表:
| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 |
|---|---|---|---|---|
| radius | number | 否 | 10 | 倒角半径。 |
| raw | boolean | 否 | false | 是否返回一个Path对象,默认值为false返回序列化后的字符串。 |
示例:
graph.addEdge({ source: rect1, target: rect2, vertices: [ { x: 100, y: 200 }, { x: 300, y: 120 }, ], connector: { name: 'rounded', args: { radius: 10, }, }, })jumpover —— 跳线连接器
跳线连接器用直线连接起点、路由点和终点,并在边与边的交叉处用跳线符号链接,是绘制 ER 图、电路图等"多线交叉"场景的利器。它的实现是内置连接器中最复杂的(src/registry/connector/jumpover.ts),整体工作流程为:
- 注册更新钩子(
setupUpdating):将当前边的视图加入graph._jumpOverUpdateList更新列表,当其他边发生变化(cell:mouseup、model:reseted)时自动触发重算,保证跳线位置始终正确。 - 筛选交叉对象:遍历图中所有边,跳过
ignoreConnectors中指定的连接器类型(默认忽略'smooth'),并且对于排在该边之后的同类型 jumpover 边不再重复检测,避免交叉处出现"双重跳线环"。 - 求交与分段:使用
findLineIntersections计算当前边线段与其他边线段的交点,再通过createJumps在交点两侧按size距离截断线段,生成标记为跳线的子线段。 - 路径组装(
buildPath):普通线段直接L连接;跳线线段则按type生成不同符号——arc用两段三次贝塞尔曲线近似半圆弧(默认),gap直接抬起画笔制造缺口,cubic用单条三次贝塞尔曲线生成抛物线状跳线。
支持的参数如下表:
| 参数名 | 参数类型 | 是否必选 | 默认值 | 参数说明 |
|---|---|---|---|---|
| type | 'arc' | 'gap' | 'cubic' | 否 | 'arc' | 跳线类型。 |
| size | number | 否 | 5 | 跳线大小。 |
| radius | number | 否 | 0 | 倒角半径。 |
| raw | boolean | 否 | false | 是否返回一个Path对象,默认值为false返回序列化后的字符串。 |
其中radius参数会作用于非跳线段落的拐角,复用与 rounded 相同的buildRoundedSegment倒角算法;当radius为0时则退化为纯直线。此外,源码中的JumpoverConnectorOptions还暴露了ignoreConnectors?: string[]选项,可自定义"不做跳线处理"的连接器名称列表,默认值为['smooth']。
实战建议:跳线效果依赖"整张图中所有边"的交叉检测,因此只有当边数量较多、交叉频繁时才建议使用;同时要注意它会为每个交叉边视图建立监听关系,在超大规模图中需评估性能开销。
自定义连接器
函数签名
连接器本质上是一个普通函数,签名为:
export type Definition<T> = ( this: EdgeView, // 边的视图 sourcePoint: Point.PointLike, // 起点 targetPoint: Point.PointLike, // 终点 routePoints: Point.PointLike[], // 路由返回的点 args: T, // 参数 edgeView: EdgeView, // 边的视图 ) => Path | string参数说明:
| 参数名 | 参数类型 | 参数说明 |
|---|---|---|
| this | EdgeView | 边的视图。 |
| sourcePoint | Point.PointLike | 起点。 |
| targetPoint | Point.PointLike | 终点。 |
| routePoints | Point.PointLike[] | 路由返回的点。 |
| args | T | 连接器参数。 |
| edgeView | EdgeView | 边的视图。 |
注意函数体可以通过this拿到EdgeView实例(如 jumpover 正是借助this.graph访问整张图的边集合),而edgeView参数与this指向同一对象,可互为补充。
编写一个 wobble 连接器
下面定义一个在路径上叠加随机抖动的wobble连接器:
export interface WobbleArgs { spread?: number raw?: boolean } function wobble( sourcePoint: Point.PointLike, targetPoint: Point.PointLike, vertices: Point.PointLike[], args: WobbleArgs, ) { const spread = args.spread || 20 const points = [...vertices, targetPoint].map((p) => Point.create(p)) let prev = Point.create(sourcePoint) const path = new Path(Path.createSegment('M', prev)) for (let i = 0, n = points.length; i < n; i += 1) { const next = points[i] const distance = prev.distance(next) let d = spread while (d < distance) { const current = prev.clone().move(next, -d) current.translate( Math.floor(7 * Math.random()) - 3, Math.floor(7 * Math.random()) - 3, ) path.appendSegment(Path.createSegment('L', current)) d += spread } path.appendSegment(Path.createSegment('L', next)) prev = next } return args.raw ? path : path.serialize() }其思路是:从起点出发,沿"当前点 → 目标点"方向以spread为步长逐步推进,每步在水平、垂直方向各施加 ±3 像素的随机偏移,从而生成一条"手绘抖动"风格的折线;支持raw参数决定返回Path对象还是序列化字符串,与内置连接器保持一致的约定。
注册并使用自定义连接器
Graph.registerConnector('wobble', wobble)registerConnector是Graph的静态方法,直接代理到连接器注册表connectorRegistry.register(见 src/graph/graph.ts),并配有对应的Graph.unregisterConnector(src/graph/graph.ts)用于反注册。注册表由 src/registry/connector/index.ts 中的Registry.create创建,内置的 5 个连接器(normal、smooth、rounded、jumpover、loop)在模块加载时以connectorRegistry.register(connectorPresets, true)的方式预注册。
注册后即可通过连接器名称使用:
edge.setConnector('wobble', { spread: 16 })也可以在addEdge的connector配置或connecting.connector全局默认中直接引用'wobble'。当传入未注册的名称时,注册表会抛出拼写建议错误(见 src/registry/registry.ts 的onNotFound逻辑),帮助快速定位笔误。
深入:连接器与几何库的协作
所有内置连接器都构建在 X6 几何库(src/geometry)之上,理解这些基础类型有助于编写更强大的自定义连接器:
Path:路径容器,通过Path.createSegment('M' | 'L' | 'C' | 'Q', ...)追加各类路径段,最终serialize()输出 SVGd字符串;Point:二维点,提供clone()、move()、rotate()、translate()、distance()等常用几何运算;Polyline/Curve/Line:分别被 normal(直线段)、smooth(贯穿曲线)、jumpover(线段求交)使用,其中 jumpover 通过Line.intersectsWithLine完成交叉检测;Path.parse:可将 SVG 路径字符串反向解析为Path对象,loop 连接器(src/registry/connector/loop.ts)即用模板字符串构造二次贝塞尔路径后调用Path.parse返回Path实例。
从 src/registry/connector/main.ts 可以看到,仓库还内置了一个文档未单独展开的loop连接器,用于自环边(起点与终点相同)的绘制,说明连接器注册表本身是开放可扩展的——你可以基于同样的机制注册任意自定义实现。
小结
连接器是 X6 边渲染体系的"最后一公里":通过connector边配置、edge.setConnector()或connecting.connector全局默认三种方式即可无缝切换内置的 normal / smooth / rounded / jumpover 四种外观;当内置方案无法满足需求时,按照(sourcePoint, targetPoint, routePoints, args) => Path | string的函数签名编写实现,再经Graph.registerConnector注册即可,整个过程与内置连接器共享同一套几何库与注册机制。相关参考实现与测试可继续阅读 src/registry/connector 目录及tests/registry/connector 下的测试用例。
【免费下载链接】X6🚀 JavaScript diagramming library that uses SVG and HTML for rendering.项目地址: https://gitcode.com/GitHub_Trending/x6/X6
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考