一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情。
前情提要: 上一期我们讲了如何创建基础图形,节点和边,这一期我们讲一讲连接柱Port。
连接桩 Port
链接桩是节点上的固定连接点,很多图应用都有链接桩,并且有些应用还将链接桩分为输入链接桩和输出连接桩。
创建节点时我们可以通过 ports 选项来配置链接桩,像下面这样:
const node = new Node({
ports: {
groups: { ... }, // 链接桩组定义
items: [ ... ], // 链接桩
}
})
// 或者
const node = new Node({
ports: [ ... ], // 链接桩
})
链接桩 Markup 也就是连接柱的样式,可以在单个链接桩、链接桩群组和节点的 portMarkup 选项三个位置指定,优先级从高到低。
知道了链接桩的 DOM 结构,我们就可以来定制链接桩的样式:
graph.addNode({
x: 60,
y: 60,
width: 160,
height: 80,
label: 'Rect With Ports',
ports: [
{
id: 'port1',
attrs: {
circle: {
r: 6,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff',
},
},
},
{
id: 'port2',
attrs: {
circle: {
r: 6,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff',
},
},
},
{
id: 'port3',
attrs: {
circle: {
r: 6,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff',
},
},
},
],
})
值得注意的是,我们给
circle 指定了 magnet: true 这个特殊属性,使链接桩在连线交互时可以被连接上。
上面代码中每个链接桩的样式都一样,显得有点冗长,我们可以通过 group 选项来设置链接桩分组,使该组中的链接桩具有相同的行为和样式。
看下面如何使用链接桩分组来定义链接桩样式:
graph.addNode({
x: 60,
y: 60,
width: 160,
height: 80,
label: 'Rect With Ports',
ports: {
groups: {
group1: {
attrs: {
circle: {
r: 6,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff',
},
},
},
},
items: [
{
id: 'port1',
group: 'group1', // 指定分组名称
},
{
id: 'port2',
group: 'group1', // 指定分组名称
},
{
id: 'port3',
group: 'group1', // 指定分组名称
},
],
},
})
链接桩标签
另外,还可以为链接桩指定标签文本。链接桩标签的 Markup 可以在单个链接桩、链接桩群组和节点的 portLabelMarkup 选项三个位置指定,优先级从高到低。
{
id: 'port1',
group: 'group1',
attrs: {
text: { // 标签选择器
text: 'port1', // 标签文本
},
},
},
连接到链接桩
graph.addEdge({
source: { x: 40, y: 100 },
target: {
cell: rect,
port: 'port1', // 链接桩 ID
},
})
graph.addEdge({
source: { x: 40, y: 100 },
target: {
cell: rect,
port: 'port2', // 链接桩 ID
},
})
graph.addEdge({
source: { x: 40, y: 100 },
target: {
cell: rect,
port: 'port3', // 链接桩 ID
},
})
实践
链接桩选项多、配置代码长,推荐的做法是,基于群组将链接桩的通用选项定义为节点的默认选项。例如我们可以定义一个矩形节点,然后为该矩形节点设置预定义的输入和输出链接桩。
Shape.Rect.define({
shape: 'my-rect',
width: 180,
height: 80,
ports: {
groups: {
// 输入链接桩群组定义
in: {
position: 'top', //定义连接柱的位置,如果不配置,将显示为默认样式
label: {
position: 'top', //定义标签的位置
},
attrs: { //定义连接柱的样式
circle: {
r: 6, //半径
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff',
},
},
},
// 输出链接桩群组定义
out: {
position: 'bottom',
label: {
position: 'bottom',
},
attrs: {
circle: {
r: 6,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff',
},
},
},
},
},
})
graph.addNode({
x: 60,
y: 50, //x,y坐标
shape: 'my-rect',
label: 'In Ports & Out Ports',
ports: [
{
id: 'port1',
group: 'in',
},
{
id: 'port2',
group: 'in',
},
{
id: 'port3',
group: 'in',
},
{
id: 'port4',
group: 'out',
},
{
id: 'port5',
group: 'out',
},
],
})
但是改了这个配置后,样式统一了,但也就无法定制其他的样式了。
position 位置
链接桩布局算法只能通过 groups 中的 position 选项来指定,因为布局算法在计算链接桩位置是需要考虑到群组中的所有连接桩,我们在单个链接桩中可以通过 args 选项来影响该链接桩的布局结果。
官方默认提供了下面几种链接桩布局算法,同时支持自定义链接桩布局算法并注册使用,点击下面的链接可以了解每种布局算法的使用方法。
'absolute'绝对定位。'left'矩形节点左侧均匀分布。'right'矩形节点右侧均匀分布。'top'矩形节点顶部均匀分布。'bottom'矩形节点底部均匀分布。'line'沿指定的线均匀分布。'ellipse'沿椭圆圆弧分布。'ellipseSpread'沿椭圆均匀分布。
官方提供的这些布局,除了absolute,都会将你的连接柱经行均匀分布。absolute的用法:
graph.addNode({
ports: {
groups: {
group1: {
position: {
name: 'absolute',
args: { x: 0, y: 0 },
},
},
},
items: [
{
group: 'group1',
args: {
x: '60%',
y: 32,
angle: 45,
},
},
]
},
})
或者
graph.addNode({
x: 100,
y: 60,
width: 280,
height: 120,
attrs: {
body: {
fill: '#f5f5f5',
stroke: '#d9d9d9',
strokeWidth: 1,
},
},
ports: {
groups: {
group1: {
attrs: {
circle: {
r: 6,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff',
},
text: {
fontSize: 12,
fill: '#888',
},
},
position: {
name: 'absolute',
},
},
},
items: [
{
id: 'port1',
group: 'group1',
// 通过 args 指定绝对位置
args: {
x: 0,
y: 60,
},
attrs: {
text: { text: '{ x: 0, y: 60 }' },
},
},
{
id: 'port2',
group: 'group1',
// 通过 args 指定绝对位置和链接桩的旋转角度
args: {
x: 0.6,
y: 32,
angle: 45,
},
// 自定义链接桩渲染的 SVG
markup: [
{
tagName: 'path',
selector: 'path',
},
],
zIndex: 10,
attrs: {
path: {
d: 'M -6 -8 L 0 8 L 6 -8 Z',
magnet: true,
fill: 'red',
},
text: {
text: '{ x: 0.6, y: 32, angle: 45 }',
fill: 'red',
},
},
},
{
id: 'port3',
group: 'group1',
// 通过 args 指定绝对位置
args: {
x: '100%',
y: '100%',
},
attrs: {
text: { text: "{ x: '100%', y: '100%' }" },
},
label: {
position: {
name: 'right',
},
},
},
],
},
})