ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

X6 连接桩布局(Port Layout)完全指南:内置算法、参数详解与自定义注册

X6 连接桩布局(Port Layout)完全指南:内置算法、参数详解与自定义注册 X6 连接桩布局Port Layout完全指南内置算法、参数详解与自定义注册【免费下载链接】X6 JavaScript diagramming library that uses SVG and HTML for rendering.项目地址: https://gitcode.com/GitHub_Trending/x6/X6连接桩Port是图编辑器节点上用于连接边的锚点而连接桩布局算法决定了这些锚点在节点上的排布方式。本文基于 X6 官方文档 连接桩布局 展开结合仓库内 port-layout 注册表源码、连接桩管理器 与单元测试系统讲解 X6 内置布局算法的配置方法、参数含义以及如何编写并注册自定义布局算法。读完本文你将能够为任意节点配置整齐的等分边排布、椭圆环绕排布、绝对定位等布局并实现属于自己的专属布局。布局算法的本质一个返回相对位置的函数在 X6 中连接桩布局算法本质上是一个函数其签名为type DefinitionT ( portsPositionArgs: T[], // 连接桩中指定的布局算法参数 elemBBox: Rectangle, // 节点的包围盒 groupPositionArgs: T, // group 中定义的默认布局算法参数 ) Result[] interface Result { position: Point.PointLike // 相对于节点的位置 angle?: number // 旋转角度 }函数接收三个参数返回一组ResultportsPositionArgs该分组下每个连接桩在items中声明的args参数数组顺序与连接桩一一对应elemBBox节点的包围盒Rectangle布局算法据此感知节点的尺寸与中心groupPositionArgsgroups分组中通过position.args声明的默认布局参数。Result中的position是相对于节点的位置而非画布绝对坐标。例如某节点在画布上的位置是{ x: 30, y: 40 }若布局返回的某个连接桩位置是{ x: 2, y: 4 }那么该连接桩渲染到画布后的实际位置是{ x: 32, y: 44 }。angle则为可选参数指定连接桩的旋转角度渲染时由视图层应用见下文渲染链路。通过 groups 配置布局通过 items 覆盖参数配置连接桩布局时只能通过ports.groups选项来指定布局算法而在items中则可以为单个连接桩提供可选的args用于覆盖分组内的默认参数graph.addNode( ..., ports: { // 连接桩分组 groups: { group1: { position: { name: xxx, // 布局算法名称 args: { }, // 布局算法的默认参数 }, }, }, // 连接桩定义 items: [ { group: group1, args: { }, // 覆盖 group1 中指定的默认参数 }, ], }, )从实现上看PortManager.getPortsLayoutByGroup 会先取出分组对应的布局函数然后分别收集每个连接桩的position.args作为portsPositionArgs与分组的position.args作为groupPositionArgs一并传给布局函数最终把返回的相对位置与连接桩 id、尺寸、attrs 等组装成LayoutResult供渲染使用。这就是分组给默认值、单桩可覆盖的底层机制。内置布局算法X6 内置了absolute、left、right、top、bottom、line、ellipse、ellipseSpread共 8 种布局算法源码集中在 src/registry/port-layout 目录main.ts 统一导出absolute、ellipse、line三个模块其中 line.ts 同时导出left/right/top/bottom四种边布局。absolute绝对定位absolute布局通过args直接指定连接桩的坐标适用于需要精确摆放锚点的场景interface AbsoluteArgs { x?: string | number y?: string | number angle?: number }名称类型必选默认值描述xstring | number0连接桩在 X 轴相对位置。ystring | number0连接桩在 Y 轴相对位置。anglenumber0连接桩旋转角度。当x和y为百分比字符串如60%或位于[0, 1]之间的数值时表示相对节点宽度/高度的百分比偏移量否则表示绝对偏移量。百分比换算的实现位于 src/common/number/number.ts 的 normalizePercentage字符串以%结尾时先除以 100 再乘以参照尺寸宽/高普通数字则原样使用未传值时视为0。这一点已被 absolute 单元测试 覆盖例如节点包围盒为(0, 0, 200, 100)时{ x: 50%, y: 60% }会得到{ x: 100, y: 60 }。使用示例graph.addNode({ ports: { groups: { group1: { position: { name: absolute, args: { x: 0, y: 0 }, }, }, }, items: [ { group: group1, args: { x: 60%, y: 32, angle: 45, }, }, ], }, })可运行示例见 站点演示源码 site/src/api/port-layout/absolute/index.tsx。left / right / top / bottom沿边线均匀排布这四种布局让连接桩沿矩形节点指定的边线均匀分布对矩形形状的节点非常友好可通过args设置偏移量与旋转角度interface SideArgs { dx?: number dy?: number angle?: number x?: number y?: number }名称类型必选默认值描述strictbooleanfalse是否严格等分均匀分布。dxnumber0沿 X 轴方向的偏移量。dynumber0沿 Y 轴方向的偏移量。anglenumber0连接桩的旋转角度。xnumber-用指定的 X 坐标覆盖计算结果中的 X 坐标。ynumber-用指定的 Y 坐标覆盖计算结果中的 Y 坐标。实现上left/right/top/bottom分别取包围盒的左/右/上/下两条对角顶点构造一条 Line再复用统一的lineLayout内部函数见 src/registry/port-layout/line.ts#L83-L104在边上取点strict: false默认时第index个桩的插值比例为(index 0.5) / length即首尾各留半个间距、连接桩居中排布strict: true时比例为(index 1) / (length 1)连接桩严格等分整条边线含端点等距。使用示例graph.addNode({ ports: { groups: { group1: { position: left, // 也支持对象写法 { name: left, args: {} } }, }, items: [ { group: group1, args: { dx: 2, }, }, ], }, })可运行示例见 site/src/api/port-layout/side/index.tsx。line沿线段均匀分布line布局允许自定义一条线段让连接桩沿该线段均匀分布适合需要把锚点排布在节点内部斜线等非边位置上的场景interface LineArgs { start?: Point.PointLike end?: Point.PointLike dx?: number dy?: number angle?: number x?: number y?: number }名称类型必选默认值描述startPoint.PointLike线段起点。endPoint.PointLike线段终点。strictbooleanfalse是否严格等分均匀分布。dxnumber0沿 X 轴方向的偏移量。dynumber0沿 Y 轴方向的偏移量。anglenumber0连接桩的旋转角度。xnumber-用指定的 X 坐标覆盖计算结果中的 X 坐标。ynumber-用指定的 Y 坐标覆盖计算结果中的 Y 坐标。start与end未指定时分别默认取包围盒的左上角getOrigin()与右下角getCorner()此时行为等价于从左上到右下的对角线排布strict的语义与边布局一致。注意start/end同样支持百分比写法通过normalizePoint换算。使用示例graph.addNode({ ports: { groups: { group1: { position: { name: line, args: { start: { x: 10, y: 10 }, end: { x: 210, y: 10 }, }, }, }, }, items: [ { group: group1, args: { dx: 2, }, }, ], }, })可运行示例见 site/src/api/port-layout/line/index.tsx。ellipse沿圆弧步进分布ellipse布局让连接桩沿椭圆或圆形弧线分布从start指定的角度开始以step为步长均匀步进interface EllipseArgs { start?: number step?: number compensateRotate?: boolean dr?: number dx?: number dy?: number angle?: number x?: number y?: number }名称类型必选默认值描述startnumber起始角度。stepnumber20步长。compensateRotatenumberfalse是否沿圆弧修正连接桩的旋转角度。drnumber0沿半径方向的偏移量。dxnumber0沿 X 轴方向的偏移量。dynumber0沿 Y 轴方向的偏移量。anglenumber0连接桩的旋转角度。xnumber-用指定的 X 坐标覆盖计算结果中的 X 坐标。ynumber-用指定的 Y 坐标覆盖计算结果中的 Y 坐标。从 ellipse.ts 的实现 看start未指定时默认0节点顶部step默认20度第index个桩的角度为start (index 0.5 - count / 2) * step即以起始角为中心向两侧对称展开适合节点已有若干桩后沿圆周逐步追加的场景compensateRotate: true时会调用ellipse.tangentTheta(p)求出该点处的切线方向作为旋转角使连接桩方向始终贴合圆弧dr表示沿半径方向朝圆心内外偏移p.move(center, dr)配合dx/dy平移可实现环带效果。使用示例动态追加 10 个连接桩const node graph.addNode({ ports: { groups: { group1: { position: { name: ellipse, args: { start: 45, }, }, }, }, }, }) Array.from({ length: 10 }).forEach((_, index) { node.addPort({ id: ${index}, group: group1, attrs: { text: { text: index } }, }) })可运行示例见 site/src/api/port-layout/ellipse/index.tsx。ellipseSpread沿椭圆整周均匀分布ellipseSpread与ellipse类似但把连接桩整周均匀铺满椭圆适合雷达图、环形菜单等场景interface EllipseSpreadArgs { start?: number compensateRotate?: boolean dr?: number dx?: number dy?: number angle?: number x?: number y?: number }名称类型必选默认值描述startnumber起始角度。compensateRotatenumberfalse是否沿圆弧修正连接桩的旋转角度。drnumber0沿半径方向的偏移量。dxnumber0沿 X 轴方向的偏移量。dynumber0沿 Y 轴方向的偏移量。anglenumber0连接桩的旋转角度。xnumber-用指定的 X 坐标覆盖计算结果中的 X 坐标。ynumber-用指定的 Y 坐标覆盖计算结果中的 Y 坐标。与ellipse的关键差异在步长计算ellipseSpread未指定step时默认取360 / portsPositionArgs.length见 ellipse.ts#L31-L42因此无论连接桩数量多少都能均匀覆盖整个椭圆一周每个桩的角度为start index * step。使用示例const node graph.addNode({ ports: { groups: { group1: { position: { name: ellipseSpread, args: { start: 45, }, }, }, }, }, }) Array.from({ length: 36 }).forEach((_, index) { node.addPort({ group: group1, id: ${index}, attrs: { text: { text: index } }, }) })可运行示例见 site/src/api/port-layout/ellipse-spread/index.tsx。自定义连接桩布局内置布局无法覆盖所有场景时可以完全按照上文布局算法是一个函数的规则自定义算法并注册到系统中。第一步实现布局函数布局函数接收三个参数连接桩参数数组、节点包围盒、分组默认参数返回每个连接桩相对节点的位置与旋转角。例如实现一个正弦分布的布局算法function sin(portsPositionArgs, elemBBox) { return portsPositionArgs.map((_, index) { const step -Math.PI / 8 const y Math.sin(index * step) * 50 return { position: { x: index * 12, y: y elemBBox.height, }, angle: 0, } }) }该算法将连接桩沿 X 轴按12像素等距排列Y 轴则按正弦曲线振幅50起伏并以节点包围盒的高度为基线。第二步注册到系统布局算法实现后必须注册到系统之后即可像内置布局算法一样使用Graph.registerPortLayout(sin, sin)Graph.registerPortLayout是 portLayoutRegistry.register 的静态快捷方式底层由 Registry.create 创建的portLayoutRegistry承载。注册表还提供了对应的Graph.unregisterPortLayout见 src/graph/graph.ts#L157。需要注意的是Registry.register 在注册同名算法且未开启force时会抛出Port layout with name xxx already registered.错误若确需覆盖可调用Graph.registerPortLayout(sin, fn, true)第三个参数force。第三步像内置算法一样使用注册以后就可以在节点配置中直接引用自定义算法名称const rect graph.addNode({ ports: { groups: { sin: { position: { name: sin, args: { start: 45, }, }, attrs: { rect: { fill: #fe854f, width: 11, }, text: { fill: #fe854f, }, circle: { fill: #fe854f, r: 5, magnet: true, }, }, }, }, }, }) Array.from({ length: 24 }).forEach(() { rect.addPort({ group: sin }) })可运行示例见 site/src/api/port-layout/sin/index.tsx。布局的底层运行链路理解布局算法的完整执行链路有助于排查定位问题参数归并PortManager.getPortsLayoutByGroup 收集分组内所有连接桩各自的args与分组的默认args并查找注册表中的布局函数。若分组未显式配置position.name则回退到portLayoutPresets.left默认左侧排布见 src/model/port.ts#L140名称校验若注册表中不存在指定名称会调用portLayoutRegistry.onNotFound抛出错误并借助拼写建议机制给出提示如Port layout with name sinx does not exist. Did you mean sin?见 src/registry/registry.ts#L117-L141坐标换算布局结果中的position经由 util.ts 的 normalizePoint / toResult 处理——normalizePoint使用NumberExt.normalizePercentage把百分比与数值统一换算为相对坐标toResult将位置、角度与原始args合并为最终PortLayoutResult渲染应用视图层 src/view/node/index.ts#L464-L488 读取metric.portLayout通过applyPortTransform将相对位置换算为画布坐标并应用旋转-(portLayout.angle || 0)最终呈现到 SVG 中。这一链路同样适用于自定义算法——你只需要关心给定参数与包围盒返回相对位置其余坐标换算、渲染与交互均由框架完成。内置布局速查算法名适用场景关键参数absolute精确摆放锚点x、y支持百分比、angleleft/right/top/bottom矩形节点四边等分排布strict、dx、dy、angle、x、yline沿自定义线段排布start、end、strict、dx、dyellipse沿圆弧以固定步长排布start、step、compensateRotate、drellipseSpread沿椭圆整周均匀排布start、compensateRotate、dr自定义任意数学分布由你的函数签名决定无论是快速接入内置布局还是为业务定制专属的锚点分布X6 的连接桩布局注册表都提供了清晰的扩展点写一个函数、注册一个名字、在groups.position.name中引用它即可完成从布局定义到节点渲染的全流程。【免费下载链接】X6 JavaScript diagramming library that uses SVG and HTML for rendering.项目地址: https://gitcode.com/GitHub_Trending/x6/X6创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表