X6 连接点(Connection Point)与锚点完全指南:从边界求交到自定义特殊连线
【免费下载链接】X6🚀 JavaScript diagramming library that uses SVG and HTML for rendering.项目地址: https://gitcode.com/GitHub_Trending/x6/X6
本指南围绕 X6 图编辑引擎中"边如何吸附到节点"这一核心机制展开,系统讲解锚点(Anchor)与连接点(Connection Point)的概念、计算流程、全局与单边配置方式,并基于仓库源码深入解析boundary、bbox、rect、anchor四种内置连接点与十余种内置锚点的全部参数。读完本文,你将掌握如何让多条连线在节点上保持固定间隔、如何让连线精准吸附任意图形边界,以及如何通过Graph.registerConnectionPoint/Graph.registerAnchor注册自定义连接点与锚点,定制出贴合业务场景的特殊连线。
先看效果:固定连接点的多连线示例
教程章节以一个可交互示例开场(对应源码site/src/tutorial/intermediate/connection-point/multi/index.tsx):当你在画布上拖动节点时,边与节点的连接位置始终保持不变,且多条边之间保留固定间隔。这与默认的连线行为(连接点随节点移动而重算)完全不同。
其实现思路正是本指南的主题:通过给每条边显式指定锚点与连接点,把"连接位置"从"跟随图形变化"转变为"固定在节点上的特定点"。
理解锚点与连接点:一条参考线的两端
两个概念的职责划分
X6 用两个独立的概念共同决定边的起点和终点:
- 锚点(Anchor):边在节点上的"瞄准点"。默认值为
center,即元素的几何中心。它只负责"指向哪里",并不直接决定最终的起止点。 - 连接点(Connection Point):决定"交点怎么算"。默认值为
boundary,即与元素边框的交点。
边的绘制过程可以拆解为三步:
- 从起点元素的锚点向终点元素的锚点连一条参考线;
- 按照
connectionPoint指定的计算方法,求出参考线与元素图形的交点; - 该交点即为边的起点/终点。
更精确地讲(参见 连接点注册表文档):
- 起点:从第一个路径点(若没有路径点则取终点元素的中心)连参考线到起点元素的锚点,再按指定连接点算法求参考线与图形的交点,即为边的起点;
- 终点:从最后一个路径点(若没有路径点则取起点元素的中心)连参考线到终点元素的锚点,再按指定连接点算法求交点,即为边的终点。
源码中的计算链路
这条计算流水线在边视图(EdgeView)中按固定顺序执行,见 src/view/edge/index.ts:
findAnchors(vertices):求出源/目标锚点(sourceAnchor/targetAnchor);findRoutePoints(vertices):求出路由点(路径点);findConnectionPoints(routePoints, sourceAnchor, targetAnchor):结合锚点与路由点计算最终连接点。
在模型层,src/model/edge.ts 的getTerminalPoint(type)会先判断终点是否为纯坐标点;若绑定的是 Cell,则调用cell.getConnectionPoint(this, type)交给节点侧解析。而全局默认值定义在 src/graph/options.ts 的Connecting配置中:
anchor: 'center', connectionPoint: 'boundary', router: 'normal', connector: 'normal',使用方式一:在connecting中全局配置
锚点与连接点都可以在图实例的connecting选项中全局配置,所有后续创建的边都会继承该默认值:
const graph = new Graph({ connecting: { // 锚点会在节点右侧中心向上偏移 10px sourceAnchor: { name: 'right', args: { dy: -10, }, }, // 锚点会在节点右侧中心向上偏移 10px targetAnchor: { name: 'right', args: { dy: -10, }, }, // 连接点直接用锚点位置,不再做边界求交 connectionPoint: 'anchor', }, })这里sourceAnchor/targetAnchor对应节点锚点(NodeAnchor),connectionPoint对应连接点(ConnectionPoint)。配置对象由name(内置类型名或自定义注册名)与args(该类型支持的参数)组成;当无参数时可直接写成字符串简写,例如connectionPoint: 'boundary'。
需要留意的是,src/graph/options.ts 还提供了更细粒度的覆盖项:sourceConnectionPoint/targetConnectionPoint、sourceEdgeAnchor/targetEdgeAnchor(边锚点 EdgeAnchor),可用于区分起点与终点采用不同算法。
使用方式二:在source/target中单边配置(优先级更高)
创建边时,可以在source、target中分别指定锚点与连接点,单边配置的优先级高于connecting中的全局配置:
graph.addEdge({ source: { cell: source, anchor: { name: 'right', args: { dy: -10, }, }, connectionPoint: 'anchor', }, target: { cell: target, anchor: { name: 'left', args: { dy: -10, }, }, connectionPoint: 'anchor', }, })边创建完成后,还可以通过edge.setSource/edge.setTarget动态修改连接点(见 connection-point 注册表文档):
edge.setSource({ cell: 'source-id', connectionPoint: { name: 'boundary', args: { sticky: true, }, }, })上述示例中锚点固定为right/left并偏移 10px、连接点直接取锚点(connectionPoint: 'anchor'),正是"多边固定连接位置、彼此保持间隔"效果的通用写法——连接点不再参与求交,边的起止点即锚点本身。
四种内置连接点详解
X6 提供四种内置连接点算法(实现统一注册于 src/registry/connection-point/main.ts 与 src/registry/connection-point/index.ts):
| 名称 | 说明 |
|---|---|
boundary | 默认值,与连接元素的实际图形边界求交 |
bbox | 与连接元素的包围盒求交 |
rect | 与连接元素的旋转后矩形区域求交 |
anchor | 直接以锚点作为连接点 |
boundary:智能识别图形边界
boundary会自动识别连接元素的真实几何形状(如<ellipse>会按椭圆求交、矩形按矩形求交),无法识别的元素(如text、<path>)则退化为包围盒求交,行为等同于bbox。其实现位于 src/registry/connection-point/boundary.ts。
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
offset | number \| Point.PointLike | 否 | 0 | 连接点的偏移量 |
stroked | boolean | 否 | true | 是否考虑图形描边宽度 |
insideout | boolean | 否 | true | 参考线位于图形内部且无交点时,是否延长参考线求交 |
extrapolate | boolean | 否 | false | 参考线位于图形外部且无交点时,是否延长参考线求交;优先级高于sticky |
sticky | boolean | 否 | false | 参考线位于图形外部且无交点时,是否取边界上距离参考线最近的点作为连接点 |
precision | number | 否 | 2 | 交点计算精度 |
selector | string | 否 | undefined | 选择器,用于指定节点内某个子元素,取其边界求交;默认取节点内第一个非<g>的子元素 |
结合源码看几个关键行为:
insideout:当锚点在图形内部、参考线与图形无交点时,若insideout === false且参考线起点被图形包围盒包含,则直接返回锚点(见boundary.ts中localBBox.containsPoint(localRef)分支);extrapolate:为true时将参考线长度延长到1e6再求交(localLine.setLength(1e6)),保证远距离也能命中边界;sticky:无交点时对矩形调用getNearestPointToPoint、对椭圆调用intersectsWithLineFromCenterToPoint、其余图形调用closestPoint,把连接点"粘"到边界最近点;stroked:为true时通过 src/registry/connection-point/util.ts 的getStrokeWidth读取stroke-width,把连接点再外扩半个描边宽度;precision:对Path类图形,路径会被细分成小段(getSegmentSubdivisions)后求交,精度越高分段越细。
提示:
boundary对复杂path图形的支持在仓库示例 examples/src/pages/edge/sticky-point.tsx 中有完整演示(sticky: true, extrapolate: true配合circle、ellipse、polygon、polyline、自定义path图标等形状)。
anchor:锚点即连接点
anchor连接点直接返回参考线端点(锚点位置),支持以下参数:
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
offset | number \| Point.PointLike | 否 | 0 | 连接点偏移量 |
其实现 src/registry/connection-point/anchor.ts 还额外支持两个可选参数:align('top' | 'right' | 'bottom' | 'left')与alignOffset,可将参考线对齐到指定方向后再取端点,源码与测试(tests/registry/connection-point/anchor.spec.ts)中均有对应逻辑。
bbox 与 rect
bbox:求参考线与元素未旋转包围盒的交点。参数:offset(默认0)、stroked(默认false,注意与boundary的默认值不同)。rect:求参考线与元素旋转后矩形区域的交点,参数与bbox一致(offset默认0、stroked默认false)。适合元素带有angle旋转角度的场景。
两种算法的参数均以对象形式传入:
const edge = graph.addEdge({ source: { cell: 'source-id', connectionPoint: { name: 'boundary', args: { sticky: true, }, }, }, target: { cell: 'target-id', connectionPoint: 'boundary', // 无参数时可简写 }, })全局默认配置同样支持完整写法与简写:
new Graph({ connecting: { connectionPoint: { name: 'boundary', args: { sticky: true, }, }, // 简写:connectionPoint: 'boundary' }, })内置锚点(NodeAnchor)速查
锚点决定了参考线从节点的哪个位置引出。X6 内置 12 种锚点,完整参数见 NodeAnchor 注册表文档,注册入口在 src/registry/node-anchor/main.ts。
| 名称 | 说明 |
|---|---|
center | 元素中心点(默认值) |
top/bottom/left/right | 元素上/下/左/右边缘的中点 |
midSide | 距离最近的那条边的中点 |
topLeft/topRight/bottomLeft/bottomRight | 元素四个角 |
orth | 正交点 |
nodeCenter | 节点中心点 |
其中center、top、bottom、left、right、四个角点共用一套参数:
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
dx | number \| string | 否 | 0 | X 轴偏移,支持绝对偏移与百分比相对偏移 |
dy | number \| string | 否 | 0 | Y 轴偏移,支持绝对偏移与百分比相对偏移 |
rotate | boolean | 否 | false | 是否使用随节点旋转的包围盒,默认不考虑旋转角度 |
midSide额外支持:
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
padding | number | 否 | 0 | 沿边方向偏移的像素距离 |
direction | 'H' \| 'V' | 否 | - | 限定连接方向,如设为H则只连接节点左右侧中点,根据相对位置自动判断 |
orth支持padding(默认0);nodeCenter支持dx、dy(默认均为0)。
锚点同样支持在创建边时指定、事后用edge.setSource/edge.setTarget修改、以及在connecting中全局配置:
const edge = graph.addEdge({ source: { cell: 'source-id', anchor: { name: 'midSide', args: { dx: 10 }, }, }, target: { cell: 'target-id', anchor: 'orth', // 无参数时简写 }, }) // 全局默认 new Graph({ connecting: { anchor: { name: 'midSide', args: { dx: 10 }, }, }, })自定义连接点与自定义锚点
当内置类型无法满足需求时,X6 允许注册任意数量的自定义实现,注册后即可像内置类型一样按名称使用。
自定义连接点
连接点定义是一个返回Point的函数:
export type Definition<T> = ( line: Line, // 参考线 view: NodeView, // 所连接节点的视图 magnet: SVGElement, // 所连接节点上的元素 args: T, // 参数 ) => Point完成定义后注册到全局注册表(内部实现为 src/registry/connection-point/index.ts 的connectionPointRegistry):
Graph.registerConnectionPoint('custom-connection-point', (line, view, magnet, args) => { // 返回连接点坐标 return new Point(0, 0) })之后即可直接按名称使用:
new Graph({ connecting: { connectionPoint: 'custom-connection-point', }, })自定义锚点
锚点定义同样是一个函数,签名为:
export type Definition<T> = ( this: EdgeView, // 边视图 nodeView: NodeView, // 节点视图 magnet: SVGElement, // magnet 元素 ref: Point.PointLike | SVGElement, // 参考点/元素 args: T, // 参数 type: Edge.TerminalType, // 边端点类型(source / target) ) => Point注册与使用方式与连接点一致:
Graph.registerAnchor('custom-anchor', (nodeView, magnet, ref, args, type) => { // 返回锚点坐标 return new Point(0, 0) }) new Graph({ connecting: { anchor: { name: 'custom-anchor', }, }, })小结
锚点与连接点是 X6 边渲染链路中决定"边从哪里出发、落在哪里"的两个关键环节:锚点负责瞄准,连接点负责求交。通过connecting全局配置与source/target单边配置(后者优先级更高),配合boundary的sticky/extrapolate/selector等参数、midSide/right等锚点的dx/dy偏移,即可稳定实现多连线等间隔、吸附任意形状边界等特殊连线效果;当内置能力不够时,还可通过注册函数的方式完全自定义连接点与锚点的计算逻辑。
建议继续阅读仓库内配套文档以获得完整参数矩阵与可交互示例:ConnectionPoint 注册表文档、NodeAnchor 注册表文档,以及边的基础用法 edge 教程。
【免费下载链接】X6🚀 JavaScript diagramming library that uses SVG and HTML for rendering.项目地址: https://gitcode.com/GitHub_Trending/x6/X6
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考