跳到主要内容

边

在本章节中主要介绍边相关的知识,通过阅读,你可以了解到

  • 添加边的方法
  • 如何配置边的形状
  • 如何在边上增加箭头
  • 如何自定义边
  • 如何通过 API 修改边

添加边

节点和边都有共同的基类 Cell,除了从 Cell 继承属性外,还支持以下选项。

属性名 类型 默认值 描述
source TerminalData - 源节点或起始点。
target TerminalData - 目标节点或目标点。
vertices Point.PointLike[] - 路径点。
router RouterData - 路由。
connector ConnectorData - 连接器。
labels Label[] - 标签。
defaultLabel Label 默认标签 默认标签。
graph.addEdge({
  shape: 'edge',
  source: 'node1',
  target: 'node2',
})

配置边

下面分别看下上面的配置如何使用。

source/target

边的源和目标节点(点)。

graph.addEdge({
  source: rect1, // 源节点
  target: rect2, // 目标节点
})

graph.addEdge({
  source: 'rect1', // 源节点 ID
  target: 'rect2', // 目标节点 ID
})

graph.addEdge({
  source: { cell: rect1, port: 'out-port-1' }, // 源节点和连接桩 ID
  target: { cell: 'rect2', port: 'in-port-1' }, // 目标节点 ID 和连接桩 ID
})

graph.addEdge({
  source: 'rect1', // 源节点 ID
  target: { x: 100, y: 120 }, // 目标点
})

vertices

路径点。边从起始点开始,按顺序经过路径点,最后到达终止点。

graph.addEdge({
  source: rect1,
  target: rect2,
  vertices: [
    { x: 100, y: 200 },
    { x: 300, y: 120 },
  ],
})
import './index.less'
import { createRoot } from 'react-dom/client'
import React from 'react'
import { Graph } from '@antv/x6'

Graph.registerNode(
  'custom-node',
  {
    inherit: 'rect', // 继承于 rect 节点
    width: 100,
    height: 40,
    markup: [
      {
        tagName: 'rect', // 标签名称
        selector: 'body', // 选择器
      },
      {
        tagName: 'image',
        selector: 'img',
      },
      {
        tagName: 'text',
        selector: 'label',
      },
    ],
    attrs: {
      body: {
        stroke: '#8f8f8f',
        strokeWidth: 1,
        fill: '#fff',
        rx: 6,
        ry: 6,
      },
      img: {
        'xlink:href':
          'https://gw.alipayobjects.com/zos/antfincdn/FLrTNDvlna/antv.png',
        width: 16,
        height: 16,
        x: 12,
        y: 12,
      },
    },
  },
  true,
)

export default class Example extends React.Component {
  private container: HTMLDivElement

  componentDidMount() {
    const graph = new Graph({
      container: this.container,
      background: {
        color: '#F2F7FA',
      },
    })

    const source = graph.addNode({
      shape: 'custom-node', // 可以直接使用上面注册过的 shape
      x: 40,
      y: 40,
      label: 'hello',
    })

    const target = graph.addNode({
      shape: 'custom-node',
      x: 300,
      y: 220,
      label: 'world',
    })

    graph.addEdge({
      source,
      target,
      attrs: {
        line: {
          stroke: '#8f8f8f',
          strokeWidth: 1,
        },
      },
      vertices: [
        { x: 100, y: 200 },
        { x: 300, y: 120 },
      ],
    })
  }

  refContainer = (container: HTMLDivElement) => {
    this.container = container
  }

  render() {
    return (
      <div className="vertices-app">
        <div className="app-content" ref={this.refContainer} />
      </div>
    )
  }
}

const root = createRoot(document.getElementById('container')!)
root.render(<Example />)
window.addEventListener('pagehide', () => root.unmount(), { once: true })

router

路由 router 将对 vertices 进一步处理,并在必要时添加额外的点,然后返回处理后的点。例如,经过 orth 路由处理后,边的每一条链接线段都是水平或垂直的。

graph.addEdge({
  source: rect1,
  target: rect2,
  vertices: [
    { x: 100, y: 200 },
    { x: 300, y: 120 },
  ],
  // 如果没有 args 参数,可以简写为 router: 'orth'
  router: {
    name: 'orth',
    args: {},
  },
})
import './index.less'
import { createRoot } from 'react-dom/client'
import React from 'react'
import { Graph } from '@antv/x6'

Graph.registerNode(
  'custom-node',
  {
    inherit: 'rect', // 继承于 rect 节点
    width: 100,
    height: 40,
    markup: [
      {
        tagName: 'rect', // 标签名称
        selector: 'body', // 选择器
      },
      {
        tagName: 'image',
        selector: 'img',
      },
      {
        tagName: 'text',
        selector: 'label',
      },
    ],
    attrs: {
      body: {
        stroke: '#8f8f8f',
        strokeWidth: 1,
        fill: '#fff',
        rx: 6,
        ry: 6,
      },
      img: {
        'xlink:href':
          'https://gw.alipayobjects.com/zos/antfincdn/FLrTNDvlna/antv.png',
        width: 16,
        height: 16,
        x: 12,
        y: 12,
      },
    },
  },
  true,
)

export default class Example extends React.Component {
  private container: HTMLDivElement

  componentDidMount() {
    const graph = new Graph({
      container: this.container,
      background: {
        color: '#F2F7FA',
      },
    })

    const source = graph.addNode({
      shape: 'custom-node', // 可以直接使用上面注册过的 shape
      x: 40,
      y: 40,
      label: 'hello',
    })

    const target = graph.addNode({
      shape: 'custom-node',
      x: 300,
      y: 220,
      label: 'world',
    })

    graph.addEdge({
      source,
      target,
      attrs: {
        line: {
          stroke: '#8f8f8f',
          strokeWidth: 1,
        },
      },
      vertices: [
        { x: 100, y: 200 },
        { x: 300, y: 120 },
      ],
      router: 'orth',
    })
  }

  refContainer = (container: HTMLDivElement) => {
    this.container = container
  }

  render() {
    return (
      <div className="router-app">
        <div className="app-content" ref={this.refContainer} />
      </div>
    )
  }
}

const root = createRoot(document.getElementById('container')!)
root.render(<Example />)
window.addEventListener('pagehide', () => root.unmount(), { once: true })

X6 默认提供了以下几种路由,点击下面的链接查看每种路由的使用方式。

另外,我们也可以注册自定义路由,详情请参考自定义路由教程。

connector

连接器 connector 将路由 router 返回的点加工成渲染边所需要的 pathData。例如,rounded 连接器将连线之间的倒角处理为圆弧倒角。

graph.addEdge({
  source: rect1,
  target: rect2,
  vertices: [
    { x: 100, y: 200 },
    { x: 300, y: 120 },
  ],
  router: 'orth',
  // 如果没有 args 参数,可以简写为 connector: 'rounded'
  connector: {
    name: 'rounded',
    args: {},
  },
})
import './index.less'
import { createRoot } from 'react-dom/client'
import React from 'react'
import { Graph } from '@antv/x6'

Graph.registerNode(
  'custom-node',
  {
    inherit: 'rect', // 继承于 rect 节点
    width: 100,
    height: 40,
    markup: [
      {
        tagName: 'rect', // 标签名称
        selector: 'body', // 选择器
      },
      {
        tagName: 'image',
        selector: 'img',
      },
      {
        tagName: 'text',
        selector: 'label',
      },
    ],
    attrs: {
      body: {
        stroke: '#8f8f8f',
        strokeWidth: 1,
        fill: '#fff',
        rx: 6,
        ry: 6,
      },
      img: {
        'xlink:href':
          'https://gw.alipayobjects.com/zos/antfincdn/FLrTNDvlna/antv.png',
        width: 16,
        height: 16,
        x: 12,
        y: 12,
      },
    },
  },
  true,
)

export default class Example extends React.Component {
  private container: HTMLDivElement

  componentDidMount() {
    const graph = new Graph({
      container: this.container,
      background: {
        color: '#F2F7FA',
      },
    })

    const source = graph.addNode({
      shape: 'custom-node', // 可以直接使用上面注册过的 shape
      x: 40,
      y: 40,
      label: 'hello',
    })

    const target = graph.addNode({
      shape: 'custom-node',
      x: 300,
      y: 220,
      label: 'world',
    })

    graph.addEdge({
      source,
      target,
      attrs: {
        line: {
          stroke: '#8f8f8f',
          strokeWidth: 1,
        },
      },
      vertices: [
        { x: 100, y: 200 },
        { x: 300, y: 120 },
      ],
      router: 'orth',
      connector: 'rounded',
    })
  }

  refContainer = (container: HTMLDivElement) => {
    this.container = container
  }

  render() {
    return (
      <div className="connector-app">
        <div className="app-content" ref={this.refContainer} />
      </div>
    )
  }
}

const root = createRoot(document.getElementById('container')!)
root.render(<Example />)
window.addEventListener('pagehide', () => root.unmount(), { once: true })

X6 默认提供了以下几种连接器,点击下面的链接查看每种连接器的使用方式。

另外,我们也可以注册自定义连接器,详情请参考自定义连接器。

labels

用于设置标签文本、位置、样式等。通过数组形式支持多标签,labels 指定的每一项都将与 defaultLabel 进行 merge 后使用。

const edge = graph.addEdge({
  source: rect1,
  target: rect2,
  labels: [
    {
      attrs: {
        label: {
          text: 'edge',
        },
      },
    },
  ],
})
// 或
const edge = graph.addEdge({
  source: rect1,
  target: rect2,
  labels: ['edge'], // 通过 labels 可以设置多个标签,当只设置标签文本是可以简化为此写法
})
// 或
const edge = graph.addEdge({
  source: rect1,
  target: rect2,
  label: 'edge', // 通过 label 设置单个标签,当只设置标签文本时可以简化为此写法
})
import './index.less'
import { createRoot } from 'react-dom/client'
import React from 'react'
import { Graph } from '@antv/x6'

Graph.registerNode(
  'custom-node',
  {
    inherit: 'rect', // 继承于 rect 节点
    width: 100,
    height: 40,
    markup: [
      {
        tagName: 'rect', // 标签名称
        selector: 'body', // 选择器
      },
      {
        tagName: 'image',
        selector: 'img',
      },
      {
        tagName: 'text',
        selector: 'label',
      },
    ],
    attrs: {
      body: {
        stroke: '#8f8f8f',
        strokeWidth: 1,
        fill: '#fff',
        rx: 6,
        ry: 6,
      },
      img: {
        'xlink:href':
          'https://gw.alipayobjects.com/zos/antfincdn/FLrTNDvlna/antv.png',
        width: 16,
        height: 16,
        x: 12,
        y: 12,
      },
    },
  },
  true,
)

export default class Example extends React.Component {
  private container: HTMLDivElement

  componentDidMount() {
    const graph = new Graph({
      container: this.container,
      background: {
        color: '#F2F7FA',
      },
    })

    const source = graph.addNode({
      shape: 'custom-node',
      x: 40,
      y: 40,
      label: 'hello',
    })

    const target = graph.addNode({
      shape: 'custom-node',
      x: 300,
      y: 220,
      label: 'world',
    })

    graph.addEdge({
      source,
      target,
      attrs: {
        line: {
          stroke: '#8f8f8f',
          strokeWidth: 1,
        },
      },
      labels: [
        {
          attrs: {
            label: {
              text: '40%',
              stroke: '#aaa',
            },
          },
          position: 0.4,
        },
        {
          attrs: {
            label: {
              text: '60%',
              stroke: '#aaa',
            },
          },
          position: 0.6,
        },
      ],
    })
  }

  refContainer = (container: HTMLDivElement) => {
    this.container = container
  }

  render() {
    return (
      <div className="labels-app ">
        <div className="app-content" ref={this.refContainer} />
      </div>
    )
  }
}

const root = createRoot(document.getElementById('container')!)
root.render(<Example />)
window.addEventListener('pagehide', () => root.unmount(), { once: true })

除了设置文本,还可以使用 Label 在边上创建一些复杂图形,我们在 API 中会详细介绍。

defaultLabel

默认标签。默认标签可以简化标签配置项,labels 指定的每一项都将与 defaultLabel 进行 merge 后使用。

使用箭头

我们定义了 sourceMarker 和 targetMarker 两个特殊属性来为边定制起始和终止箭头。例如,对 Shape.Edge 我们可以通过 line 选择器来指定起始和终止箭头。

内置箭头

X6 提供了以下几种内置箭头,使用时只需要指定箭头名和参数(可省略)即可。

graph.addEdge({
  shape: 'edge',
  source: [100, 100],
  target: [500, 500],
  attrs: {
    line: {
      sourceMarker: 'block', // 实心箭头
      targetMarker: {
        name: 'ellipse', // 椭圆
        rx: 10, // 椭圆箭头的 x 半径
        ry: 6, // 椭圆箭头的 y 半径
      },
    },
  },
})
import './index.less'
import { createRoot } from 'react-dom/client'
import React from 'react'
import { Graph } from '@antv/x6'

const markers = [
  'block',
  'classic',
  'diamond',
  'circle',
  'circlePlus',
  'ellipse',
  'cross',
  'async',
]

export default class Example extends React.Component {
  private container: HTMLDivElement

  componentDidMount() {
    const graph = new Graph({
      container: this.container,
      background: {
        color: '#F2F7FA',
      },
    })

    markers.forEach((marker, i) => {
      graph.addEdge({
        sourcePoint: [120, 20 + i * 40],
        targetPoint: [400, 20 + i * 40],
        label: marker,
        attrs: {
          line: {
            sourceMarker: marker,
            targetMarker: marker,
            stroke: '#8f8f8f',
            strokeWidth: 1,
          },
        },
      })
    })
  }

  refContainer = (container: HTMLDivElement) => {
    this.container = container
  }

  render() {
    return (
      <div className="native-marker-app ">
        <div className="app-content" ref={this.refContainer} />
      </div>
    )
  }
}

const root = createRoot(document.getElementById('container')!)
root.render(<Example />)
window.addEventListener('pagehide', () => root.unmount(), { once: true })

提示

X6 中边默认自带 classic 箭头,如果要去掉,可以将 targetMarker 设置为 null。

自定义箭头

我们也可以通过 tagName 指定的 SVG 元素来渲染箭头,例如下面我们使用 <path> 元素来渲染箭头,箭头默认继承边的填充色 fill 和边框色 stroke。

graph.addEdge({
  shape: 'edge',
  source: [100, 100],
  target: [500, 500],
  attrs: {
    line: {
      sourceMarker: {
        tagName: 'path',
        d: 'M 20 -10 0 0 20 10 Z',
      },
      targetMarker: {
        tagName: 'path',
        fill: 'yellow', // 使用自定义填充色
        stroke: 'green', // 使用自定义边框色
        strokeWidth: 2,
        d: 'M 20 -10 0 0 20 10 Z',
      },
    },
  },
})

提示

我们的起始箭头和终止箭头使用了相同的 d 属性,这是因为我们会自动计算箭头方向,简单来说,我们在定义箭头时,只需要定义一个向左指向坐标原点的箭头即可。

import './index.less'
import { createRoot } from 'react-dom/client'
import React from 'react'
import { Graph } from '@antv/x6'

export default class Example extends React.Component {
  private container: HTMLDivElement

  componentDidMount() {
    const graph = new Graph({
      container: this.container,
      background: {
        color: '#F2F7FA',
      },
    })

    graph.addEdge({
      source: [100, 140],
      target: [400, 140],
      label: 'custom-marker',
      attrs: {
        line: {
          sourceMarker: {
            tagName: 'path',
            d: 'M 20 -10 0 0 20 10 Z',
          },
          targetMarker: {
            tagName: 'path',
            stroke: '#D94111',
            strokeWidth: 2,
            fill: '#90C54C',
            d: 'M 20 -10 0 0 20 10 Z',
          },
          stroke: '#8f8f8f',
          strokeWidth: 1,
        },
      },
    })
  }

  refContainer = (container: HTMLDivElement) => {
    this.container = container
  }

  render() {
    return (
      <div className="custom-marker-app">
        <div className="app-content" ref={this.refContainer} />
      </div>
    )
  }
}

const root = createRoot(document.getElementById('container')!)
root.render(<Example />)
window.addEventListener('pagehide', () => root.unmount(), { once: true })

更多箭头的案例和定制技巧请参考 API。

定制边

和节点一样,我们可以通过 markup 和 attrs 来定制边的形状和样式,也可以注册自定义边来达到复用效果。X6 默认的边 Shape.Edge 中定义了 line(代表 path 元素)和 wrap(代表透明的 path 元素,用于响应交互)两个选择器。我们在创建边时可以像下面这样定义边的样式。

import './index.less'
import { createRoot } from 'react-dom/client'
import React from 'react'
import { Graph } from '@antv/x6'

Graph.registerEdge('double-edge', {
  inherit: 'edge',
  markup: [
    {
      tagName: 'path',
      selector: 'outline',
      attrs: {
        fill: 'none',
      },
    },
    {
      tagName: 'path',
      selector: 'line',
      attrs: {
        fill: 'none',
        cursor: 'pointer',
      },
    },
  ],
  attrs: {
    line: {
      connection: true,
      stroke: '#dddddd',
      strokeWidth: 4,
      strokeLinejoin: 'round',
      targetMarker: {
        tagName: 'path',
        stroke: '#000000',
        d: 'M 10 -3 10 -10 -2 0 10 10 10 3',
      },
    },
    outline: {
      connection: true,
      stroke: '#000000',
      strokeWidth: 6,
      strokeLinejoin: 'round',
    },
  },
})

Graph.registerEdge('shadow-edge', {
  inherit: 'edge',
  markup: [
    {
      tagName: 'path',
      selector: 'shadow',
      attrs: {
        fill: 'none',
      },
    },
    {
      tagName: 'path',
      selector: 'line',
      attrs: {
        fill: 'none',
        cursor: 'pointer',
      },
    },
  ],
  attrs: {
    line: {
      connection: true,
      stroke: '#dddddd',
      strokeWidth: 20,
      strokeLinejoin: 'round',
      targetMarker: {
        name: 'path',
        stroke: 'none',
        d: 'M 0 -10 -10 0 0 10 z',
        offsetX: -5,
      },
      sourceMarker: {
        name: 'path',
        stroke: 'none',
        d: 'M -10 -10 0 0 -10 10 0 10 0 -10 z',
        offsetX: -5,
      },
    },
    shadow: {
      connection: true,
      refX: 3,
      refY: 6,
      stroke: '#000000',
      strokeOpacity: 0.2,
      strokeWidth: 20,
      strokeLinejoin: 'round',
      targetMarker: {
        name: 'path',
        d: 'M 0 -10 -10 0 0 10 z',
        stroke: 'none',
        offsetX: -5,
      },
      sourceMarker: {
        name: 'path',
        stroke: 'none',
        d: 'M -10 -10 0 0 -10 10 0 10 0 -10 z',
        offsetX: -5,
      },
    },
  },
})

export default class Example extends React.Component {
  private container: HTMLDivElement

  componentDidMount() {
    const graph = new Graph({
      container: this.container,
      background: {
        color: '#F2F7FA',
      },
    })

    graph.addEdge({
      shape: 'double-edge',
      source: [100, 100],
      target: [400, 100],
    })

    graph.addEdge({
      shape: 'shadow-edge',
      source: [100, 180],
      target: [400, 180],
    })
  }

  refContainer = (container: HTMLDivElement) => {
    this.container = container
  }

  render() {
    return (
      <div className="registry-app">
        <div className="app-content" ref={this.refContainer} />
      </div>
    )
  }
}

const root = createRoot(document.getElementById('container')!)
root.render(<Example />)
window.addEventListener('pagehide', () => root.unmount(), { once: true })

修改边

和节点类似,在渲染完成之后,我们还可以通过 API 修改边的所有属性。我们会常用到下面两个方法:

  • edge.prop(path, value),详细使用见 prop。
  • edge.attr(path, value),详细使用见 attr。

下面我们看一下 X6 默认提供的边的 prop。

const edge = graph.addEdge({
  source: [200, 140],
  target: [500, 140],
  label: 'edge',
})
console.log(edge.prop())

// 输出结果
{
  "shape": "edge",
  "attrs": {
    "lines": {
      "connection": true,
      "strokeLinejoin": "round"
    },
    "wrap": {
      "strokeWidth": 10
    },
    "line": {
      "stroke": "#333",
      "strokeWidth": 2,
      "targetMarker": "classic"
    }
  },
  "id": "9d5e4f54-1ed3-429e-8d8c-a1526cff2cd8",
  "source": {
    "x": 200,
    "y": 140
  },
  "target": {
    "x": 500,
    "y": 140
  },
  "labels": [{
    "attrs": {
      "label": {
        "text": "edge"
      }
    }
  }],
  "zIndex": 1
}

从上面结果可以看到,prop 是处理后的一份新的配置,它的值可以通过方法进行更新,更新之后,边会立即刷新到最新状态。为了更快捷的修改边的 attrs,X6 提供了 attr 方法。

edge.prop('target', { x: 300, y: 300 }) // 修改终点
edge.attr('line/stroke', '#ccc') // 修改边颜色,等价于 edge.prop('attrs/line/stroke', '#ccc')
import './index.less'
import { createRoot } from 'react-dom/client'
import React from 'react'
import { Graph, Color } from '@antv/x6'
import { Button } from 'antd'

const commands = [
  {
    key: 'prop',
    label: 'prop',
  },
  {
    key: 'attr',
    label: 'attr',
  },
]

export default class Example extends React.Component {
  private container: HTMLDivElement
  private graph: Graph

  componentDidMount() {
    const graph = new Graph({
      container: this.container,
      background: {
        color: '#F2F7FA',
      },
    })

    const source = graph.addNode({
      shape: 'rect',
      x: 40,
      y: 100,
      width: 100,
      height: 40,
      label: 'hello',
      attrs: {
        body: {
          stroke: '#8f8f8f',
          strokeWidth: 1,
          fill: '#fff',
          rx: 6,
          ry: 6,
        },
      },
    })

    const target = graph.addNode({
      shape: 'rect',
      x: 340,
      y: 100,
      width: 100,
      height: 40,
      label: 'hello',
      attrs: {
        body: {
          stroke: '#8f8f8f',
          strokeWidth: 1,
          fill: '#fff',
          rx: 6,
          ry: 6,
        },
      },
    })

    graph.addEdge({
      source,
      target,
      attrs: {
        line: {
          stroke: '#8f8f8f',
          strokeWidth: 1,
        },
      },
    })

    this.graph = graph
  }

  change = (command: string) => {
    const edges = this.graph.getEdges()
    switch (command) {
      case 'prop':
        edges.forEach((edge) => {
          const x = Math.floor(Math.random() * 600)
          const y = Math.floor(Math.random() * 200)
          edge.prop('vertices', [[x, y]])
        })
        break
      case 'attr':
        edges.forEach((edge) => {
          const color = Color.randomHex()
          edge.attr('line/stroke', color)
        })
        break
      default:
        break
    }
  }

  refContainer = (container: HTMLDivElement) => {
    this.container = container
  }

  render() {
    return (
      <div className="prop-app">
        <div className="app-btns">
          <Button.Group>
            {commands.map((item) => (
              <Button onClick={() => this.change(item.key)} key={item.key}>
                {item.label}
              </Button>
            ))}
          </Button.Group>
        </div>
        <div className="app-content" ref={this.refContainer} />
      </div>
    )
  }
}

const root = createRoot(document.getElementById('container')!)
root.render(<Example />)
window.addEventListener('pagehide', () => root.unmount(), { once: true })
© Copyright 2026 Ant Group Co., Ltd..备案号:京ICP备15032932号-38
全站搜索
文档与示例输入关键词开始搜索

输入关键词,查找文档、API 与示例

↑↓选择↵打开Esc关闭