基础
- 注意版本,x6不同版本对React的要求是不同的,也需要注意插件版本,不同版本的x6的api可能是不同的
- x6 是一个图引擎,多用来画多节点关系的图
- 最重要的概念就是 节点
Node
和 边Edge
- 节点和节点通过边链接,边从哪里出发,到哪里去,就需要连接桩,其实就是连接点
- 可使用React和vue来自定义节点,也可以使用默认的svg节点
- 有很多扩展插件,内置功能来实现节点的新增,删除,复制等
- 每个节点和边都要唯一的 id ,节点被复制后,新节点的id也是不一样的,唯一的
- 节点和边的父类是 Cell对象,都要x和y属性,表示位置,都要shape属性,边就是edge,node是自定义的或者是svg的(比如Rect矩形)
- 画布上每个对象都要层级 zindex,和网页的一样,不设置的话,后者会在前者上,zindex越大,层级越高
基本使用
<div id="container"></div>
import {
Graph } from "@antv/x6";
const graph = new Graph({
container: document.getElementById("container"),
width: 800,
height: 600,
background: {
color: "#F2F7FA",
},
});
画布对象
画布对象是整个结果,包含都有的东西的引用,可用来设置宽度,背景色,栅格样式,开启是否移动缩放,监听事件(节点的增加和删除等),操作节点和边等,
比如我的画布配置是
const graphConfig = {
connecting: {
router: {
name: 'normal',//’manhattan‘:智能正交路径
args: {
// offset: 25,
// direction: 'H',
},
},
allowMulti: false, // 是否允许在相同的起始节点和终止之间创建多条边,默认为 true。
allowBlank: false,
sourceAnchor: {
name: "right", // 锚点会在节点右侧中心往上偏移 10px
args: {
dy: 0,
},
},
targetAnchor: {
name: "left", // 锚点会在节点右侧中心往上偏移 10px
args: {
dy: 0,
},
},
connectionPoint: "anchor",
},
// 鼠标拖动画布
panning: true,
// 鼠标缩放画布
mousewheel: true,
background: {
color: '#F2F7FA',
},
grid: {
visible: true,
type: 'doubleMesh',
args: [
{
color: '#eee', // 主网格线颜色
thickness: 1, // 主网格线宽度
},
{
color: '#ddd', // 次网格线颜色
thickness: 1, // 次网格线宽度
factor: 4, // 主次网格线间隔
},
],
},
}
以下为常用的方法
/**
* 使用插件
*/
graph.use(某插件)
/**
* 通过id找cell
*/
graph.getCellById("1234")
/**
** 复制一个节点,并带位移粘贴,如果一个节点有很多子节点,使用内置方法是最好的,避免大量运算
*/
graph.copy([node], {
deep: true})
graph.paste({
offset: {
dx: 22, dy: 22}})
/**
** 获取当前画布左上角的坐标,使用场景是新建一个节点,在最上角,否则不好找
*/
graph.graphToLocal()
/**
** 从json数据生产整个画布
*/
graph.fromJSON(json)
/**
** 画布导出json数据
*/
graph.toJSON()
/**
** 节点操作
*/
// 得到所有的节点
g.getNodes();
// 新建节点,并放到画布上
graph.addNode()
// 新建节点,不放到画布上,使用场景比如,节点被拖入画布
graph.createNode()
// 新建边,并放到画布上
graph.addEdge()
// 新建边,不放到画布上
graph.createEdge()
/**
** 常用事件
*/
// 监听节点的连接事件,得到边
graph.on('edge:connected', ({
isNew, edge }) => {
})
// 节点的双击事件
graph.on('node:dblclick', ({
node }) => {
})
// 节点的单击事件
graph.on('node:click', ({
node }) => {
})
// 监听节点里,子节点的变化
graph.on('cell:change:children', (args) => {
})
// 监听节点嵌入,就是被拖入父节点的事件
graph.on('node:embedded', (args) => {
})
Cell
就是节点和边的父类,有一些公用的方法
// 删除
cell.remove()
// 设置尺寸
cell.setSize()
// 设置位置
cell.setPosition()
cell.clone()
// 位置尺寸
cell.getBBox()
// 上级
cell.parent
// 下级
cell.children
// 显示和隐藏
cell.show()
cell.hide()
群组
就是父节点里有子节点, 子节点会和父节点一起被拖动
let g = new Graph({
container: this.ref.current,
...graphConfig,
// 通过 embedding对象来控制群组,实现能不能拖拽进入父节点的功能
embedding: {
enabled: true,
findParent({
node }) {
const bbox = node.getBBox();
return this.getNodes().filter((n) => {
if (node.shape === 'custom-react-node') {
return false;
}
const data = n.getData();
if (data && data.parent) {
const targetBBox = n.getBBox();
return bbox.isIntersectWithRect(targetBBox);
}
return false;
});
},
},
});
// 给节点,添加子节点
node.addChildren()
使用React自定义节点
节点自身的引用,画布的引用都可以取到,data就类似副作用,使用它来更新节点
import {
register } from '@antv/x6-react-shape';
export class NodeProcess extends Component {
toggle = () => {
const {
node} = this.props;
const data = node.getData();
let nextValue = !data.open;
node.setData({
open: nextValue})
// 控制自身的长宽
if (nextValue) {
node.prop('size', {
width: data.width || 720,
height: data.height || 360
});
} else {
node.prop('size', size);
}
// 子节点的显示隐藏
if (Array.isArray(node.children)) {
node.children.forEach(n => {
if (nextValue) {
n.show()
if (n.children && n.children.length) {
n.children.forEach(n1 => {
n1.show()
})
}
} else {
n.hide()
if (n.children && n.children.length) {
n.children.forEach(n1 => {
n1.hide()
})
}
}
})
}
}
render() {
const {
node, graph ,disabled} = this.props;
const label = node.prop('label');
const data = node.getData();
return (
<div>
{
data.open ? (
<div className="node-process-wrapper" style={
{
width: data.width || 720, height: data.height || 360}}>
<div className={
'node-process'}>
{
label}
<RightOutlined onClick={
() => this.toggle(false)}/>
</div>
</div>
) : (
<div className={
'node-process'}>
{
label}
{
data.sub.length >= 0 && <RightOutlined onClick={
() => this.toggle(true)}/>
}
</div>
)
}
</div>
)
}
}
register({
shape: "custom-react-node",
width: 100,
height:
100,
effect: ["data"],
component: NodeProcess,
});
const node = graph.addNode({
shape: "custom-react-node",
x: 60,
y: 100,
data: {
progress: 30,
},
});
setInterval(() => {
const {
progress } = node.getData<{
progress: number }>();
node.setData({
progress: (progress + 10) % 100,
});
}, 1000);
插件
更多内容可以看官网文档
对齐线
import {
Snapline } from "@antv/x6-plugin-snapline";
const graph = new Graph({
background: {
color: "#F2F7FA",
},
});
graph.use(
new Snapline({
enabled: true,
})
);
复制粘贴
import {
Clipboard } from "@antv/x6-plugin-clipboard";
const graph = new Graph({
background: {
color: "#F2F7FA",
},
});
graph.use(
new Clipboard({
enabled: true,
})
);
快捷键
import {
Clipboard } from "@antv/x6-plugin-clipboard";
const graph = new Graph({
background: {
color: "#F2F7FA",
},
});
graph.use(
new Clipboard({
enabled: true,
})
);
拖拽
import {
Dnd } from "@antv/x6-plugin-dnd";
const graph = new Graph({
background: {
color: "#F2F7FA",
},
});
const dnd = new Dnd({
target: graph,
});
export default () => {
const startDrag = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
// 该 node 为拖拽的节点,默认也是放置到画布上的节点,可以自定义任何属性
const node = graph.createNode({
shape: "rect",
width: 100,
height: 40,
});
dnd.start(node, e.nativeEvent);
};
return (
<ul>
<li onMouseDown={
startDrag}></li>
</ul>
);
};
缩放
import {
Transform } from '@antv/x6-plugin-transform'
graph
.use(
new Transform({
resizing: true,
rotating: true,
})
)
历史
import {
History } from '@antv/x6-plugin-history'
graph.use(new History())