Skip to content

X6

🕒 Published at:

说明

适合画连线的图,并能做一些复杂操作

例如:

markupConfig,就是配置里的markupConfig

attrsConfig,就是配置里的attrsConfig

依次类推

下载与注意事项

npm install @antv/x6 --save

https://x6.antv.antgroup.com/zh/examples/layout/tree/#mindmap

初始化

javascript
import { Graph } from '@antv/x6'
const container = document.getElementById('container');
const clientRect = container.getBoundingClientRect();
const width = clientRect.width || 1200;
const height = clientRect.height || 500;
const graph = new Graph({
    width,height,container,
    ...config
})

配置

基本上,不需要args参数时,都可以简写

config

base

选项类型描述示例
width/heightstring宽/高
containerDOM/string挂载的元素或它的id
virtualboolean是否只渲染可视区域内容
asyncboolean是否启用异步渲染
autoresizeboolean是否监听容器大小
gridbooleanObject
backgroundObject配置背景
panningbooleanObject
mousewheelbooleanObject
scalingObject画布的最大最小缩放级别
connectingObject交互connecting:connectingCfg
highlightingObject高亮器,支持以下配置项
1. default 默认高亮选项
2. embedding 拖动节点进行嵌入操作过程中,节点可以被嵌入时被使用。
3. nodeAvailable 连线过程中,节点可以被链接时被使用。
4. magnetAvailable 连线过程中,连接桩可以被链接时被使用。
5. magnetAdsorbed 连线过程中,自动吸附到连接桩时被使用。
highlighting: {
magnetAvailable: {
name: 'stroke', // 边框高亮器
name: 'classname' // 样式名高亮器
}
}

connectingCfg

交互

配置项类型描述示例
snapbooleanstring
allowBlank
/ Loop
/ Node
/ Port
/ Multi
boolean(Graph, args)=>boolean
highlightboolean拖动边时,是否高亮所有可用连接桩或节点
routerstring/object边的路径点
vertices 做进一步转换处理,
并在必要时添加额外的路径点
router:{
args:{},
name:'normal', //默认路由
//{name:'er', //实体关系路由,Z字形
//name:'orth', //正交路由
//name:'oneSide'', //受限正交路由
//name:'manhattan'', //智能正交路由
//name:'metro'', //智能地铁线路由
}
createEdge(Graph,args)=>Nilable<Edge>通过该方法自定义新建边的样式
connectorstring/object连接器
validateEdge(Graph,args)=>boolean当停止拖动边时校验这个边是否有效,
false则会删除这条边
validateMagnet(Graph,args)=>boolean校验哪些元素可作为磁力点(magnet),
validateConnection(Graph,args)=>boolean校验哪些元素可被连接
anchorObject/String节点锚点
edgeAnchorObject/String边锚点
source/targetEdgeAnchorEdgeAnchorOptions当连接到边时,起点/终点 边的锚点
targetAnchorNodeAnchorOptions当连接到节点时,
组合

elementCfg

元素配置项

基本上,不需要args参数时,都可以简写

选项类型描述示例
idstring节点/边的唯一标识,
推荐使用具备业务意义的 ID,
默认使用自动生成的 UUID。
zIndexNumber层级,一般用来设置边,
防止边遮挡节点
markupMarkup节点/边的 SVG/HTML 片段。markup:markupConfig
attrsAttr.CellAttrs节点/边属性样式。attrs:attrsConfig
shapestring元素类型
节点默认值为rect,
边对应的默认值为edge
toolsToolItemToolItem[]Tools
dataany节点/边关联的业务数据。
用来存储与配置无关的数据,
通过cell.getData()获取节点/边关联的业务数据。
x/ynumber节点的定位x:120,y:10
labelstring节点的文本
component使用组件component:MyComponent
anglenumber节点的旋转角度。
portsobject连接桩。见节点/连接桩
source/targetTerminalData起点/终点 连接的节点id以及连接桩/锚点信息。target: {
cell: 'target-id',
anchor: 'orth', // 节点锚点, 边锚点
},
verticesPoint.PointLike[]路径点。
边从起点开始,
按顺序经过路径点,
最后到达终点。
vertices: [
routerRouterData路由。
对 vertices 进一步处理,
并在必要时添加额外的路径点

通过name指定路由类型
支持args,无args时可简写
router:{
args:{},
name: 'normal', // 默认路由
//{name: 'er', // 实体关系路由,Z字形
//name: 'orth', // 正交路由
//name: 'oneSide'', // 受限正交路由
//name: 'manhattan'',// 智能正交路由
//name: 'metro'', // 智能地铁线路由
}
connectorConnectorData连接器。如何连接路径点connector:{
zIndexnumber节点/边在画布中的层级,默认根据节点/边添加顺序自动确定。
visibleboolean节点/边是否可见。
parentstring父节点。
childrenstring[]子节点/边。

节点类型

rect

ellipse

text-block

image

markupConfig

自定义这个东西添加的元素

配置项描述类型常见配置示例
tagName对应元素类型(与节点类型一致)tagName:'rect',
selector当前元素的唯一选择器,selector:'body',
groupSelector群组选择器,groupSelector: 'line',
textContenttext类型的元素显示的文字
children子元素children:[与父元素一样的配置]

attrsConfig

javascript
//attrs通过指定选择器为元素修改样式
const attrsConfig= {
    body: tagCfg,
    label:tagCfg
    //选择器:tagCfg
}

tagCfg

名称类型备注示例
x/yNumber向x/y轴平移多少像素
fillString填充颜色
strokeString边的颜色
strokeWidthNumber边的粗细
opacityNumber透明度
fontSizeNumber字体大小
fontFamilyString字体
cursorString同css cursor
textAnchorString文本基于其锚点的水平对齐方式,
值:start,middle(默认),end
textVerticalAnchorString文本基于其锚点的垂直对齐方式,
值:top,middle(默认),bottom
refString指定 参考元素,默认为bodyref: 'body', //指定参考元素
refX/YString向x/y轴平移的百分比或偏移量refX: '-50%', // 向左偏移参考元素宽度一半
refX2/Y2String通过refX/Y当需要同时指定偏移量和百分比时使用refX: '-50%',
refX2:20
refDx/DyString设置元素坐标为相对于 参考元素 右下角的x,y偏移量
refRx/yString椭圆半径refRy: '25%',椭圆 y 轴半径为参考元素高度的 1/4
refCx/yString椭圆圆心refCx: '50%', // 椭圆中心 x 坐标为参考元素宽度一半,即位于参考元素的中心
refWidth/HeightString宽高为 参考元素 的百分比或加多少像素refWidth: '50%', // 宽度为参考元素宽的一半

refWidth: '20', // 宽度为参考元素宽+20px
refWidth2/Height2String同refWidth/Height,需同时指定时使用refWidth: '50%',
refWidth: '20', // 宽度为参考元素宽的一半+20px
source/targetMarkerStringObject

箭头类型

tools

直接写在元素的tools配置项上

或者通过 元素.addTools(config)添加

工具类型描述
boundary渲染一个包围元素的矩形。注意,该工具仅仅渲染一个矩形,不带任何交互。
button在指定位置处渲染一个按钮,支持自定义按钮的点击交互。
button-remove在指定的位置处,渲染一个删除按钮,点击时删除对应的元素
node-editor(节点特有)提供文本编辑功能。
edge-editor(边特有)提供文本编辑功能。
vertices(边特有)路径点工具,在路径点位置渲染一个小圆点,拖动小圆点修改路径点位置,双击小圆点删除路径点,在边上单击添加路径点。
segments(边特有)线段工具。在边的每条线段的中心渲染一个工具条,可以拖动工具条调整线段两端的路径点的位置。
source/target-arrowhead(边特有)在边的起点或终点渲染一个图形(默认是箭头),拖动该图形来修改边的起点或终点。

使用

javascript
const tools = [{
  name: 'button', //工具类型
  args: {
    // tools直接在markup中定义元素的样式,格式为[{...,attrs:{样式配置}}]
    markup: [{...markupConfig[0],attrs: arrsConfig.body}],
    x: '100%',y: '100%',
    offset: { x: -20, y: -20 },
    onClick({ cell }: { cell: Cell }) {}, //cell对应点击的节点
  },
}]

// 创建元素时添加小工具
graph.addNode/Edge({ 
    ...,
    tools
})


//判断是否有某个别名的工具
node/edge.hasTools(别名)
// 创建元素后添加小工具
node/edge.addTools(tools,别名)
//移除所有工具
node/edge.removeTools();

鼠标悬浮添加工具,离开移除

javascript
graph.on('edge:mouseenter', ({ cell }) => {
  cell.addTools(
    [
      'vertices',        //转折点工具
      'segments',        //调整边工具
      'button-remove',   //移除按钮
      {
        name: 'target-arrowhead',
        args: {
          attrs: {
            fill: 'red'
          }
        }
      }
    ],
    'onhover'
  );
});

graph.on('edge:mouseleave', ({ cell }) => {
  if (cell.hasTools('onhover')) {
    cell.removeTools();
  }
});

自定义

javascript
//在原有工具基础上,进行修改
const MyButton = Button.define<Button.Options>()
Graph.registerNodeTool('my-btn', MyButton, true)

//自己实现一个,特别难,搞不了
Graph.registerNodeTool('button', Button)

方法

原生方法

方法名说明
graph.getNodes/Edges/Ports()获取所有节点/边/连接桩
graph.addNode/Edge/Port(config)添加节点/边/连接桩
graph.use(plugin)使用插件
graph.resize(width,height)resize画布大小
graph.centerContent()将画布中元素居中展示
graph.zoomToFit({ maxScale: 1 })将画布中的元素自适应到刚好放下
graph.getNeighbors(cell,
{ incoming: true, outgoing: false})
获取节点相邻的节点,incoming是输入节点,outgoing是输出节点
graph.getPredecessors/Successors(cell)获取节点的所有前序/后续节点,
graph.isNeighbor/Predecessor/Successor(cell,cell2)判断cell2是不是cell的相邻/前序/后续节点
graph.getConnectedEdges(node,
{ incoming: true, outgoing: false})
获取节点所连接的边,incoming是输入边,outgoing是输出边
node.removePort/Edge(Id)删除连接桩/边
node.removePorts/Edges()删除所有连接桩/边
node.portProp(portId, 'attrs/circle/stroke', color)更新连接桩
cell.prop('size', { width: 120, height: 50 })修改元素的配置,这里是修改节点size
cell.prop(path)获取元素的配置,没有path就是获取所有的配置
cell.attr('rect/fill', '#ccc')修改元素的属性,这里是修改节点填充色,等价于 cell.prop('attrs/rect/fill', '#ccc')
graph.to/fromJSON()导出/导入数据

依赖keybord的方法

方法名说明
graph.bindKey(key,callback)添加快捷键
graph.unbindKey(key)解绑快捷键
graph.clearKeys()解绑所有快捷键

依赖history的方法

方法名说明
graph.toggleHistory(boolean)启用/禁用history功能
graph.canUndo/Redo()获取是否可以撤销/恢复
graph.undo/redo()撤销/恢复

鼠标事件

需要注意的是,这里的 mousemove 事件和通常的鼠标移动事件有所区别,它需要在鼠标按下后移动鼠标才能触发。

除了 mouseenter 和 mouseleave 外,事件回调函数的参数都包含鼠标相对于画布的位置 x、y 和鼠标事件对象 e 等参数。

事件cell 节点/node 节点/port 连接桩/edge 边/边blank 画布空白区域
单击cell/node/node:port/edge/blank:click
双击cell/node/node:port/edge/blank:dblclick
右键cell/node/node:port/edge/blank:contextmenu
鼠标按下cell/node/node:port/edge/blank:mousedown
移动鼠标cell/node/node:port/edge/blank:mousemove
鼠标抬起cell/node/node:port/edge/blank:mouseup
鼠标滚轮cell/node/-/edge/blank:mousewheel
鼠标进入cell/node/node:port/edge/graph:mouseenter
鼠标离开cell/node/node:port/edge/graph:mouseleave
javascript
graph.on('cell:click', ({ e, x, y, cell, view }) => {})
graph.on('node:click', ({ e, x, y, node, view }) => {})
graph.on('edge:click', ({ e, x, y, edge, view }) => {})
graph.on('blank:click', ({ e, x, y }) => {})
graph.on('cell:mouseenter', ({ e, cell, view }) => {})
graph.on('node:mouseenter', ({ e, node, view }) => {})
graph.on('edge:mouseenter', ({ e, edge, view }) => {})
graph.on('graph:mouseenter', ({ e }) => {})

插件

Keyboard&History&Clipboard&Selection

Keyboard

History

Clipboard

Selection

javascript
//用于快捷键
import { Keyboard } from '@antv/x6-plugin-keyboard'
//启用历史操作
import { History } from '@antv/x6-plugin-history'
//启用粘贴板
import { Clipboard } from '@antv/x6-plugin-clipboard'
//启用框选器
import { Selection } from '@antv/x6-plugin-selection'
import { Graph } from '@antv/x6'

new Graph(config)
  .use(new Selection())
  .use(new Keyboard())
  .use(new Clipboard())
  .use(new History())

// 快捷键与事件
//#region 复制粘贴 需要Clipboard插件
graph.bindKey(['meta+c', 'ctrl+c'], () => {
  const cells = graph.getSelectedCells()
  if (cells.length)graph.copy(cells)
  return false
})
graph.bindKey(['meta+x', 'ctrl+x'], () => {
  const cells = graph.getSelectedCells()
  if (cells.length) graph.cut(cells)
  return false
})
graph.bindKey(['meta+v', 'ctrl+v'], () => {
  if (!graph.isClipboardEmpty()) {
    const cells = graph.paste({ offset: 32 })
    graph.cleanSelection() //清除选中
    graph.select(cells)    //选中哪些元素
  }
  return false
})
//#endregin

//#region 撤销恢复  需要History插件
// undo撤销
graph.bindKey(['meta+z', 'ctrl+z'], () => {
  if (graph.canUndo())graph.undo()
  return false
})
// redo恢复
graph.bindKey(['meta+shift+z', 'ctrl+shift+z'], () => {
  if (graph.canRedo())graph.redo()
  return false
})
//#endregion


// 全选 需要Selection插件
graph.bindKey(['meta+a', 'ctrl+a'], () => {
  const nodes = graph.getNodes()
  if (nodes)graph.select(nodes)
})

// 删除
graph.bindKey('backspace', () => {
  const cells = graph.getSelectedCells()
  if (cells.length)graph.removeCells(cells)
})

// zoom
graph.bindKey(['ctrl+1', 'meta+1'], () => {
  const zoom = graph.zoom()
  if (zoom < 1.5)graph.zoom(0.1)
})
graph.bindKey(['ctrl+2', 'meta+2'], () => {
  const zoom = graph.zoom()
  if (zoom > 0.5)graph.zoom(-0.1)
})

Transform&Snapline&MiniMap&Export

Transform

Snapline

MiniMap

Export

javascript
//可用于改变节点大小,旋转节点等
import { Transform } from '@antv/x6-plugin-transform'
//对齐线
import { Snapline } from '@antv/x6-plugin-snapline'
//缩略图
import { MiniMap } from '@antv/x6-plugin-minimap'
//导出画布
import { Export } from '@antv/x6-plugin-export'
import { Graph } from '@antv/x6'
const graph = new Graph({
  background: {
    color: '#F2F7FA',
  },
})
graph
  .use(
    new MiniMap({
      container: document.getElementById('minimap'),
    }),
  )
  .use(new Export())
  .use(new Snapline())
  .use(
    new Transform({
      resizing: true,
      rotating: true,
    }),
  )

Stencil

Stencil

javascript
//用于启动那种拖拽添加节点的菜单
import { Stencil } from '@antv/x6-plugin-stencil'


// #region 初始化 stencil
const stencil = new Stencil({
  title: '流程图',
  target: graph,
  stencilGraphWidth: 200,
  stencilGraphHeight: 180,
  collapsable: true,
  groups: [{
      title: '基础流程图',
      name: 'group1'
  }]
})
document.getElementById('stencil')!.appendChild(stencil.container)
// #endregion

//注册一堆节点,假设是r1,...r6
//加载节点到group1分组
stencil.load([r1, r2, r3, r4, r5, r6], 'group1')

节点

注册节点

javascript
graph.addNode(elementConfig)

自定义节点

javascript
import React from 'react'
import { Graph } from '@antv/x6'
//import { register } from '@antv/x6-vue-shape';
import { register } from '@antv/x6-react-shape'

//react组件
const MyComponent = ({ node }) => {
  const color = node.prop('color')
  return (<div style={{ background: color}} />)
}

//vue组件
const myVueComponent={
    template:'<div>测试</div>',
    inject:['getNode'] //通过getNode方法获取节点实例,再去获取节点的信息
    mounted(){
        const node = this.getNode();
        //监听节点的data改变
        node.on('change:data', ({ current }) => {
          this.data = current;
        });  
    },
    created() {
      this.data = this.getNode().getData();
    }
}}

//注册一个节点类型
register({
  shape: 'custom-node',
  width: 120,height: 50,
  effect: ['color'],
  component: MyComponent,
})
const graph = new Graph({ container: document.getElementById('container')})
//使用该自定义类型添加节点
const source = graph.addNode({
  x: 120, y: 50,
  shape: 'custom-node',
})

连接桩

默认/自定义/自定义复用

javascript
//1. 默认样式 连接桩默认为circle,这里仅展示⚪的连接桩
const defaultPort={ id: 'port1' }

//2. 自定义连接桩样式
const portConfig={
  id: 'port2', //连接桩id
  //如果不想用⚪的,需要markup指定用哪些元素绘制连接桩,markup具体解释看节点
  markup: markupConfig,
  attrs: attrsConfig
}
//将具有相同行为和外观的连接桩归为同一组,并通过 group 选项来设置分组
//3. 使用group将自定义样式分组,方便复用
const groupPortConfig={
    groups: {group1: portConfig},
    items: [
      { id: 'port1', group: 'group1' },
      { id: 'port2', group: 'group1' },
    ]
}
const rect = graph.addNode({
  ...,
  //ports: [defaultPort,portConfig],
  ports: groupPortConfig,
})
//元素.addProt(config)/removePort(portId/port)
rect.addPort(portConfig)

悬浮显示/移开隐藏

javascript
register({
  shape: 'custom-node',
  width: 25,
  height: 25,
  effect: ['color'],
  component: node,
  ports: {
    groups: {
      top: {
        position: 'top',
        attrs: {
          circle: {
            r: 4,
            magnet: true,
            style: {
              visibility: 'hidden'
            }
          }
        }
      },
    },
    items: [
      { id: 'port1', group: 'top' },
    ]
  }
});

graph.on('node:mouseenter', ({ cell }) => {
  cell.getPorts().forEach(port => {
    cell.setPortProp(port.id, 'attrs/circle/style/visibility', 'visable');
  });
});
graph.on('node:mouseleave', ({ cell }) => {
  cell.getPorts().forEach(port => {
    cell.setPortProp(port.id, 'attrs/circle/style/visibility', 'hidden');
  });
});

边(连接线)

路由(router):https://x6.antv.antgroup.com/api/registry/router

连接器(connector):https://x6.antv.antgroup.com/api/registry/connector#jumpover

边工具:https://x6.antv.antgroup.com/api/registry/edge-tool

高亮器:https://x6.antv.antgroup.com/api/registry/highlighter

注册边

javascript
graph.addEdge({
  //source为边的起点,target为终点
  source: { x: 40, y: 150 },
  target: {
    cell: rect, //cell对应元素,这里是连接到矩形
    port: 'port2', // 连接桩 ID,每个元素一圈的小点,为连接桩
  },
  //连接器类型,定义边如何连接起始点、目标点和中间顶点
      //rounded 圆角
      //smooth 用于 单条的平滑曲线
  connector:{name:'rounded'},
  //定义一些固定的转折点,不受router影响
  vertices: [{ x: 240, y: 140 }],
  //路由,自动计算边的转折点, manhattan 智能路由,会避开node
  router :{name: 'manhattan'},
  //边的连接器类型
  attrs: attrConfig,
})

布局

dagre层次布局

javascript
import { DagreLayout } from '@antv/layout'
const dagreLayout = new DagreLayout({
  type: 'dagre',
  rankdir: 'LR',
  align: 'UR',
  ranksepFunc:(e)=> 35, //层次间距
  nodesepFunc:(e)=> 15, //节点间距
})
const model = dagreLayout.layout(data)
graph.fromJSON(model)