logo

X6

  • Tutorials
  • API
  • Examples
  • Q&A
  • Change Log
  • XFlow
  • Productsantv logo arrow
  • 2.x
  • Graph
    • Graph
    • Grid
    • Background
    • Panning
    • Mousewheel
    • Viewport Transformation
    • Coordinate Systems
  • Element
    • Cell
    • Node
    • Edge
    • Labels
    • Arrow
    • Element Attributes
    • Interaction
  • MVC
    • Model
    • View
  • Extension
    • Node Tools
    • Edge Tools
    • Routing
    • Connector
    • Node Anchor
    • Edge Anchor
    • Connection Point
    • Port Layout Algorithm
    • Port Label Layout
    • Attributes
    • Highlighter
    • Filter

Port Layout Algorithm

Previous
Connection Point
Next
Port Label Layout

Resource

Ant Design
Galacea Effects
Umi-React Application Framework
Dumi-Component doc generator
ahooks-React Hooks Library

Community

Ant Financial Experience Tech
seeconfSEE Conf-Experience Tech Conference

Help

GitHub
StackOverflow

more productsMore Productions

Ant DesignAnt Design-Enterprise UI design language
yuqueYuque-Knowledge creation and Sharing tool
EggEgg-Enterprise-class Node development framework
kitchenKitchen-Sketch Tool set
GalaceanGalacean-Interactive solution
xtechLiven Experience technology
© Copyright 2025 Ant Group Co., Ltd..备案号:京ICP备15032932号-38

Loading...

The port layout algorithm is a function with the following signature, which returns the relative position of the port relative to the node. For example, if a node is located at { x: 30, y: 40 } on the canvas, and the returned position of a port is { x: 2, y: 4 }, then the port will be rendered at { x: 32, y: 44 } on the canvas.

type Definition<T> = (
portsPositionArgs: T[], // layout algorithm parameters specified in the port
elemBBox: Rectangle, // bounding box of the node
groupPositionArgs: T, // default layout algorithm parameters defined in the group
) => Result[]
interface Result {
position: Point.PointLike // relative position to the node
angle?: number // rotation angle
}

Note that when configuring the port ports, we can only configure the layout algorithm through the groups option, and provide optional layout algorithm parameters args in items to affect the layout result.

graph.addNode(
...,
ports: {
// port group
groups: {
group1: {
position: {
name: 'xxx', // layout algorithm name
args: { }, // default parameters of the layout algorithm
},
},
},
// port definition
items: [
{
groups: 'group1',
args: { }, // override the default parameters specified in group1
},
],
},
)

Let's take a look at how to use the built-in port layout algorithms and how to customize and register custom layout algorithms.

Built-in Layouts

absolute

Absolute positioning, specifying the port position through args.

interface AbsoluteArgs {
x?: string | number
y?: string | number
angle?: number
}
NameTypeRequiredDefault ValueDescription
xstring | number0Relative position on the X-axis.
ystring | number0Relative position on the Y-axis.
anglenumber0Rotation angle.

When x and y are percentage strings or between [0, 1], they represent the percentage offset in the width and height directions, otherwise, they represent absolute offsets.

graph.addNode({
ports: {
groups: {
group1: {
position: {
name: 'absolute',
args: { x: 0, y: 0 },
},
},
},
items: [
{
group: 'group1',
args: {
x: '60%',
y: 32,
angle: 45,
},
},
],
},
})

left, right, top, bottom

Ports are evenly distributed along the specified edge of the rectangle, and left, right, top, and bottom are four layout algorithms that are very friendly to rectangular nodes. You can set the offset and rotation angle through args.

interface SideArgs {
dx?: number
dy?: number
angle?: number
x?: number
y?: number
}
NameTypeRequiredDefault ValueDescription
strictbooleanfalseWhether to strictly distribute evenly.
dxnumber0Offset in the X-axis direction.
dynumber0Offset in the Y-axis direction.
anglenumber0Rotation angle.
xnumber-Override the calculated X-coordinate with the specified value.
ynumber-Override the calculated Y-coordinate with the specified value.
graph.addNode({
ports: {
groups: {
group1: {
position: 'left',
},
},
items: [
{
group: 'group1',
args: {
dx: 2,
},
},
],
},
})

line

Ports are evenly distributed along the line segment.

interface LineArgs {
start?: Point.PointLike
end?: Point.PointLike
dx?: number
dy?: number
angle?: number
x?: number
y?: number
}
NameTypeRequiredDefault ValueDescription
startPoint.PointLikeStart point of the line segment.
endPoint.PointLikeEnd point of the line segment.
strictbooleanfalseWhether to strictly distribute evenly.
dxnumber0Offset in the X-axis direction.
dynumber0Offset in the Y-axis direction.
anglenumber0Rotation angle.
xnumber-Override the calculated X-coordinate with the specified value.
ynumber-Override the calculated Y-coordinate with the specified value.
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,
},
},
],
},
})

ellipse

Ports are distributed along the ellipse, starting from the start angle, with a step size of step.

interface EllipseArgs {
start?: number
step?: number
compensateRotate?: boolean
dr?: number
dx?: number
dy?: number
angle?: number
x?: number
y?: number
}
NameTypeRequiredDefault ValueDescription
startnumberStart angle.
stepnumber20Step size.
compensateRotatenumberfalseWhether to compensate for the rotation angle of the ellipse.
drnumber0Offset in the radial direction.
dxnumber0Offset in the X-axis direction.
dynumber0Offset in the Y-axis direction.
anglenumber0Rotation angle.
xnumber-Override the calculated X-coordinate with the specified value.
ynumber-Override the calculated Y-coordinate with the specified value.
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 } },
})
})

ellipseSpread

Uniformly distributes connection points along an ellipse, starting from the specified angle start.

interface EllipseSpreadArgs {
start?: number
compensateRotate?: boolean
dr?: number
dx?: number
dy?: number
angle?: number
x?: number
y?: number
}
NameTypeRequiredDefault ValueDescription
startnumberStarting angle.
compensateRotatenumberfalseWhether to adjust the rotation angle of the connection points along the arc.
drnumber0Offset along the radial direction.
dxnumber0Offset along the X-axis direction.
dynumber0Offset along the Y-axis direction.
anglenumber0Rotation angle of the connection points.
xnumber-Override the X-coordinate of the calculated result with a specified X-coordinate.
ynumber-Override the Y-coordinate of the calculated result with a specified Y-coordinate.
const node = graph.addNode({
ports: {
groups: {
group1: {
position: {
name: 'ellipseSpread',
args: {
start: 45,
},
},
},
},
},
})
Array.from({ length: 36 }).forEach(function (_, index) {
ellipse.addPort({
group: 'group1',
id: `${index}`,
attrs: { text: { text: index } },
})
})

Custom Connection Point Layout

A connection point layout algorithm is a function with the following signature, which returns the relative position of each connection point relative to the node. For example, if a node is located at { x: 30, y: 40 } on the canvas, and the returned position of a connection point is { x: 2, y: 4 }, then the rendered position of the connection point on the canvas would be { x: 32, y: 44 }.

type Definition<T> = (
portsPositionArgs: T[], // Layout algorithm parameters specified in the connection points
elemBBox: Rectangle, // Node bounding box
groupPositionArgs: T, // Default layout algorithm parameters defined in the group
) => Result[]
interface Result {
position: Point.PointLike // Relative position to the node
angle?: number // Rotation angle
}

We can create a custom layout algorithm according to the above rules, for example, implementing a sine distribution layout algorithm:

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,
}
})
}

After implementing the layout algorithm, we need to register it to the system. After registration, we can use it like the built-in layout algorithms.

Graph.registerPortLayout('sin', sin)

After registration, we can use it like the built-in layout algorithms:

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' })
})
Args
x
0.6
y
32
angle
45
Args
position
strict
dx
0
dy
0
angle
45
Args
strict
dx
0
dy
0
angle
45
Args
start
45
step
20
dr
0
dx
0
dy
0
angle
45
Args
start
45
dr
0
dx
0
dy
0
angle
45