使用 AntV X6 版本 2.x,下面官网地址

Vue 节点 | X6 (antgroup.com)

path 路径节点,通过 svg 的 d 值来显示 svg

svg 在线编辑器_svg 矢量图在线制作工具-易点在线矢量图形编辑器 (wxeditor.com)

使用场景 | X6 (antgroup.com)

拖动一个图标去看它的源码里,复制 d 里面的值,放入 path 节点里的 path 属性值里面

1
<path id="svg_1" d="m199.50248,160.86426l0.19807,-0.63855l0.80193,-0.28418l0.80193,0.28418l0.19807,0.63855l-0.55497,0.51208l-0.89007,0l-0.55497,-0.51208z" stroke-width="1.5" stroke="#000" fill="#fff"/>

path 节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
id: "node3",
shape: "path",
x: 180,
y: 150,
width: 40,
height: 40,
label: "svg图",
path: "m193,160.31151l22.53615,0l6.96384,-19.86229l6.96385,19.86229l22.53615,0l-18.2321,12.27543l6.96421,19.86229l-18.23211,-12.27576l-18.2321,12.27576l6.96421,-19.86229l-18.2321,-12.27543",
attrs: {
body: {
fill: "#efdbff",
stroke: "#9254de",
},
},
},

核心

引入的插件

1
2
3
4
5
6
7
8
9
10
import { Graph, Shape } from '@antv/x6' // 导入 @antv/x6 库中的 Graph 和 Shape 类
import { Stencil } from '@antv/x6-plugin-stencil' // 导入拖拽式元素选择插件
import { Transform } from '@antv/x6-plugin-transform' // 导入变换操作插件
import { Selection } from '@antv/x6-plugin-selection' // 导入选元素插件
import { Snapline } from '@antv/x6-plugin-snapline' // 导入图形对齐插件
import { Keyboard } from '@antv/x6-plugin-keyboard' // 导入键盘事件处理插件
import { Clipboard } from '@antv/x6-plugin-clipboard' // 导入复制和粘贴图形元素插件
import insertCss from 'insert-css' // 导入动态插入 CSS 函数
import { History } from '@antv/x6-plugin-history' // 导入图形编辑历史记录插件

第 1 步,绘制画布

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
initGraph() {
// 创建一个 Graph 实例,参数是一个配置对象
this.graph = new Graph({
container: document.getElementById('container'), // 画布容器
background: false, // 背景(透明)
autoResize: true, //自动更新大小

// 网格配置
grid: {
type: 'mesh', // 网格类型为网点(点阵)形式
size: 10, // 网格大小 10px
visible: true, // 渲染网格背景
args: {
color: '#eeeeee', // 网格线/点颜色
thickness: 2, // 网格线宽度/网格点大小
},
},

mousewheel: {
enabled: true, // 启用鼠标滚轮缩放功能
zoomAtMousePosition: true, // 缩放中心点在鼠标位置
modifiers: 'ctrl', // 按住 Ctrl 键进行缩放
minScale: 0.5, // 最小缩放比例
maxScale: 3, // 最大缩放比例
},

panning: {
enabled: true, // 允许平移功能
modifiers: 'alt', // 按住 Alt 键进行平移
},

// 配置连线规则
connecting: {
router: 'manhattan', // 连线的默认路由器为曼哈顿算法
connector: {
name: 'rounded', // 连接器形状为圆角
args: {
radius: 8, // 圆角半径
},
},
anchor: 'center', // 连线默认锚点为中心点
connectionPoint: 'anchor', // 连线默认连接点为锚点
allowBlank: false, // 不允许空连接
snap: {
radius: 20, // 连接吸附的半径范围
},
createEdge() {
// 创建连线时返回一个新的 Edge 对象
return new Shape.Edge({
attrs: {
line: {
stroke: '#A2B1C3', // 连线颜色
strokeWidth: 2, // 连线宽度
targetMarker: {
name: 'block', // 目标箭头形状为方块
width: 12, // 箭头宽度
height: 8, // 箭头高度
},
},
},
zIndex: 0, // 层级设置为 0
});
},
validateConnection({ targetMagnet }) {
// 验证连接是否有效,目标连接点必须存在
return !!targetMagnet;
},

},

highlighting: {
magnetAdsorbed: {
name: 'stroke', // 线条描边样式为高亮
args: {
attrs: {
fill: '#5F95FF', // 线条填充颜色
stroke: '#5F95FF', // 线条描边颜色
},
},
},
},
});

// 使用插件
this.graph
.use(
new Transform({
// 平移、缩放、旋转等变换操作
resizing: true, // 允许调整大小
rotating: true, // 允许旋转
})
)
.use(
new Selection({
// 选择和操纵图形。提供了选择区域、单选等功能
rubberband: true, // 启用选择框
showNodeSelectionBox: true, // 显示节点的选择框
})
)
.use(new Snapline()) // 可用于对齐图形元素
.use(new Keyboard()) // 允许通过键盘快捷键来控制图形编辑
.use(new Clipboard()) // 提供了对图形的复制和粘贴操作
.use(new History()); // 历史记录撤销使用

this.graph.centerContent(); // 画布内容居中显示
},

第 2 步,初始化连接桩

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//初始化默认连接桩
initPorts() {
this.ports = {
groups: {
top: {
position: 'top',
attrs: {
circle: {
r: 4,
magnet: true,
stroke: '#5F95FF',
strokeWidth: 1,
fill: '#fff',
style: {
visibility: 'hidden',
},
},
},
},
right: {
position: 'right',
attrs: {
circle: {
r: 4,
magnet: true,
stroke: '#5F95FF',
strokeWidth: 1,
fill: '#fff',
style: {
visibility: 'hidden',
},
},
},
},
bottom: {
position: 'bottom',
attrs: {
circle: {
r: 4,
magnet: true,
stroke: '#5F95FF',
strokeWidth: 1,
fill: '#fff',
style: {
visibility: 'hidden',
},
},
},
},
left: {
position: 'left',
attrs: {
circle: {
r: 4,
magnet: true,
stroke: '#5F95FF',
strokeWidth: 1,
fill: '#fff',
style: {
visibility: 'hidden',
},
},
},
},
},
items: [
{
group: 'top',
},
{
group: 'right',
},
{
group: 'bottom',
},
{
group: 'left',
},
],
}
},

第 3 步,注册自定义节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
enrollNode() {
// 注册自定义模板节点 'custom-image'
Graph.registerNode(
'custom-image',
{
width: 62, // 节点宽度
height: 62, // 节点高度
markup: [
{
tagName: 'rect',
selector: 'body', // 节点外部矩形
},
{
tagName: 'image',
},
{
tagName: 'text',
selector: 'label',
},
],
attrs: {
body: {
stroke: 'green', // 外部矩形边框颜色
fill: 'pink', // 外部矩形填充颜色
},
image: {
refWidth: '100%', // 图片宽度和节点宽度一致
refHeight: '100%', // 图片高度和节点高度一致
},
label: {
refX: 20,
refY: 82,
textAnchor: 'center',
textVerticalAnchor: 'bottom',
fontSize: 16,
fill: '#fff', // 文字颜色
},
},
ports: { ...this.ports }, // 节点的连接点配置
},
true // 是否强制覆盖同名节点类型
);

},

第四步, 初始化侧边栏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
initStencil() {
// 创建一个 Stencil 实例
this.stencil = new Stencil({
title: '接线图', // Stencil 标题
target: this.graph, // 目标 Graph 实例
stencilGraphWidth: 200, // Stencil 绘图区宽度
stencilGraphHeight: 0, // Stencil 绘图区高度(0 表示自适应高度)
collapsable: true, // 是否可折叠

groups: [
// 定义分组
// {
// title: "基础流程图",
// name: "group1",
// graphHeight: 50,
// },
// 系统设计图分组
{
title: '系统设计图',
name: 'group2',
graphHeight: 0, // 分组内部绘图区高度(0 表示自适应高度)
layoutOptions: {
rowHeight: 70, // 分组内部行高
},
},
],
layoutOptions: {
columns: 2, // 列数
columnWidth: 80, // 列宽度
rowHeight: 55, // 行高度
},
});

// 将 Stencil 实例的容器添加到指定的 DOM 元素中
document.getElementById('stencil').appendChild(this.stencil.container);

// 插入样式
this.insertCss();
},

第 4 步,绘制左侧栏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
initStencil() {
// 创建一个 Stencil 实例
this.stencil = new Stencil({
title: '接线图', // Stencil 标题
target: this.graph, // 目标 Graph 实例
stencilGraphWidth: 200, // Stencil 绘图区宽度
stencilGraphHeight: 0, // Stencil 绘图区高度(0 表示自适应高度)
collapsable: true, // 是否可折叠

groups: [
// 定义分组
// {
// title: "基础流程图",
// name: "group1",
// graphHeight: 50,
// },
// 系统设计图分组
{
title: '系统设计图',
name: 'group2',
graphHeight: 0, // 分组内部绘图区高度(0 表示自适应高度)
layoutOptions: {
rowHeight: 70, // 分组内部行高
},
},
],
layoutOptions: {
columns: 2, // 列数
columnWidth: 80, // 列宽度
rowHeight: 55, // 行高度
},
});

// 将 Stencil 实例的容器添加到指定的 DOM 元素中
document.getElementById('stencil').appendChild(this.stencil.container);
// 插入样式,修改了左侧栏底色
this.insertCss();
},

第 5 步,添加监听事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
initEvent() {
// 当鼠标进入节点时,显示连接点
this.graph.on('node:mouseenter', () => {
const container = document.getElementById('graph-container');
const ports = container.querySelectorAll('.x6-port-body');
this.showPorts(ports, true);
});

// 当鼠标离开节点时,隐藏连接点
this.graph.on('node:mouseleave', () => {
const container = document.getElementById('graph-container');
const ports = container.querySelectorAll('.x6-port-body');
this.showPorts(ports, false);
});

// 节点点击事件
this.graph.on('cell:click', ({ cell }) => {
// 清除之前选中节点的样式
this.curCel ? this.curCel.attr('body/stroke', null) : null;
this.curCel ? this.curCel.attr('line/stroke', '#c0c0c0') : '#c0c0c0';
// 设置当前选中节点的样式为红色
this.curCel = cell;
this.curCel.attr('body/stroke', 'red');
this.curCel.attr('line/stroke', 'red');
// 获取节点的文字标签(可能在text/text中,也可能在label/text中)
this.formData.nodeName = cell.getAttrs()?.text?.text
? cell.getAttrs()?.text?.text
: cell.getAttrs()?.label?.text
? cell.getAttrs()?.label?.text
: null;
// 获取节点的名称
this.formData.Name = cell.getAttrs()?.data?.Name;
});

// 快捷键与事件
this.graph.bindKey(['meta+c', 'ctrl+c'], () => {
// 复制选中的单元格
const cells = this.graph.getSelectedCells();
if (cells.length) {
this.graph.copy(cells);
}
return false;
});

this.graph.bindKey(['meta+x', 'ctrl+x'], () => {
// 剪切选中的单元格
const cells = this.graph.getSelectedCells();
if (cells.length) {
this.graph.cut(cells);
}
return false;
});

this.graph.bindKey(['meta+v', 'ctrl+v'], () => {
// 粘贴剪贴板中的内容
if (!this.graph.isClipboardEmpty()) {
const cells = this.graph.paste({ offset: 32 });
this.graph.cleanSelection();
this.graph.select(cells);
}
return false;
});

this.graph.bindKey('delete', () => {
// 删除选中的单元格
const cells = this.graph.getSelectedCells();
if (cells.length) {
this.graph.removeCells(cells);
}
});

this.graph.bindKey(['ctrl+1', 'meta+1'], () => {
// 放大画布
const zoom = this.graph.zoom();
if (zoom < 1.5) {
this.graph.zoom(0.1);
}
});

this.graph.bindKey(['ctrl+2', 'meta+2'], () => {
// 缩小画布
const zoom = this.graph.zoom();
if (zoom > 0.5) {
this.graph.zoom(-0.1);
}
});

this.graph.bindKey(['meta+z', 'ctrl+z'], () => {
// 撤销操作
if (this.graph.canUndo()) {
this.graph.undo();
}
return false;
});

this.graph.bindKey(['meta+shift+z', 'ctrl+shift+z'], () => {
// 重做操作
if (this.graph.canRedo()) {
this.graph.redo();
}
return false;
});

this.graph.bindKey(['meta+a', 'ctrl+a'], () => {
// 全选
const nodes = this.graph.getNodes();
if (nodes) {
this.graph.select(nodes);
}
});
},

第六步,加载数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
load(){
this.$http({
url: this.$http.adornUrl('/topoImg/loader/queryImgList'),
method: 'post',
data: this.$http.adornData({}),
}).then(({ data }) => {
if (data && data.code === 0) {
this.imgList = data.list
const encounteredKeys = {}
//相同名称的设备只取其一
for (let index = 0; index < this.imgList.length; index++) {
const item = this.imgList[index]
if (!encounteredKeys[item.imgKey]) {
encounteredKeys[item.imgKey] = true
this.imageNodes.push(
this.graph.createNode({ //调用自定义节点
shape: 'custom-image',
label: item.imgKey,
attrs: {
data: {
deviceName: '',
filePositionOptions: '下居中',
valueLamp: '0',
},
image: {
'xlink:href': item.imgContent.startsWith('img')
? this.$http.adornUrl('/') + item.imgContent
: item.imgContent,
},
},
})
)
}
}

this.imageNodes.push(
this.graph.createNode({
shape: 'custom-text',
attrs: {
data: {
deviceName: '',
filePositionOptions: '中居中',
valueLamp: '0',
},
image: {
'xlink:href': '',
},
},
})
)
// console.log("左侧的数据是", this.imageNodes);
this.stencil.load(this.imageNodes, 'group2')
} else {
this.imgList = []
}
this.load(this.receiveId)
})


}

//获取画布数据
load(id) {
if (!id) {
return
}
this.$http({
url: this.$http.adornUrl('/topo/loader/getInfo'),
method: 'post',
data: this.$http.adornData({
id: id,
}),
}).then(({ data }) => {
if (data && data.code === 0) {
if (data.data.x6json === undefined || data.data.x6json === '') {
return
}
this.graph.fromJSON(JSON.parse(data.data.x6json))
// let topojson = data.data.topojson;
// let x6json = data.data.x6json;
// console.log("---------------------X--");
// console.log("topojson值", JSON.parse(JSON.parse(topojson)));
// console.log("-------------X----------");
// console.log("x6json值", JSON.parse(x6json));
//下面先遍历页面的图,然后再去遍历连接线
// nodeS.nodeDataArray.map((item) => {
// const [x, y] = item.loc.split(" ").map((value) => parseInt(value));
// // console.log("x和y的值是", x, y);
// this.graph.addNode({
// shape: "custom-image",
// label: item.imgKey,
// id: item.key,
// x: x,
// y: y,
// width: item.width,
// height: item.height,
// data: {
// size: item.size,
// loc: item.loc,
// imgKey: item.imgKey,
// valContent: item.valContent,
// },
// attrs: {
// image: {
// "xlink:href": item.source.startsWith("img")
// ? this.$http.adornUrl("/") + item.source
// : item.source,
// },
// },
// });
// });
//添加连接线
// nodeS.linkDataArray.map((item) =>
// this.graph.addEdge({
// source: { cell: item.from },
// target: { cell: item.to },
// attrs: {
// line: {
// stroke: "#c0c0c0",
// strokeWidth: 2,
// targetMarker: {
// name: "block",
// width: 12,
// height: 8,
// },
// },
// },
// zIndex: 0,
// })
// );
}
})
},

补充

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
  //修改画布内置的样式,正常css无法修改
insertCss() {
return insertCss(`
#container {
display: flex;
border: 1px solid #dfe3e8;
}
#stencil {
width: 180px;
height: 100%;
position: relative;
border-right: 1px solid #dfe3e8;
}
#graph-container {
width: calc(100% - 180px);
height: 100%;
}
.x6-widget-stencil {
background-color: #fff;
}
.x6-widget-stencil-title {
background-color: #fff;
}
.x6-widget-stencil-group-title {
background-color: #fff !important;
}
.x6-widget-transform {
margin: -1px 0 0 -1px;
padding: 0px;
border: 1px solid #239edd;
}
.x6-widget-transform > div {
border: 1px solid #239edd;
}
.x6-widget-transform > div:hover {
background-color: #3dafe4;
}
.x6-widget-transform-active-handle {
background-color: #3dafe4;
}
.x6-widget-transform-resize {
border-radius: 0;
}
.x6-widget-selection-inner {
border: 1px solid #239edd;
}
.x6-widget-selection-box {
opacity: 0;
}
`)
},


props: ['id'],
mounted() {
this.receiveId = this.$props.id
this.initGraph() //初始化画布
this.initPorts() //初始化连接桩
this.enrollNode() //注册自定义节点
this.initStencil() //初始化左侧栏ui
this.initEvent() //添加监听事件
this.queryApiList() //查找接口列表
this.loadData() //加载数据
},


<!-- 下面是节点属性框 -->
<div v-if="drawer" class="node_attr_box">
<div style="font-size: 20px; padding: 10px 5px; background-color: slategray">
<span>组态属性</span>
<span style="float: right" class="close_icon"><i class="el-icon-close" @click="drawer = false"></i></span>
</div>
<el-form :model="nodeForm">
<h3>节点设置</h3>
<el-form-item label="设备ID" prop="idNum">
<el-input v-model="nodeForm.idNum" size="mini" style="width: 100px"></el-input>
</el-form-item>
<hr />
<h3>文本设置</h3>
<el-form-item label="文本" prop="nodeName">
<el-input v-model="nodeForm.nodeName" @change="onNameChange" size="mini" style="width: 100px"></el-input>
</el-form-item>
</el-form>
<hr />
</div>


遇到问题

gojs 和 x6 的图形显示不符合预期

在 X6 中,网格是渲染/移动节点的最小单位,默认是 10px ,也就是说位置为 { x: 24, y: 38 } 的节点渲染到画布后的实际位置为 { x: 20, y: 40 }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// 构建画布
myDiagram = $(
sx.Diagram,
"myDiagramp", // 必须指定或引用 HTML 元素
{
// 是否显示网格
grid: $(
sx.Panel,
"Grid",
$(sx.Shape, "LineH", { stroke: "lightgray", strokeWidth: 0.5 }), // 水平辅助线,浅灰色
$(sx.Shape, "LineH", {
stroke: "gray",
strokeWidth: 0.5,
interval: 10,
}), // 水平主要线,灰色,每隔10个单位绘制一条
$(sx.Shape, "LineV", { stroke: "lightgray", strokeWidth: 0.5 }), // 垂直辅助线,浅灰色
$(sx.Shape, "LineV", {
stroke: "gray",
strokeWidth: 0.5,
interval: 10,
}) // 垂直主要线,灰色,每隔10个单位绘制一条
),
// 只读模式
isReadOnly: false,
// 初始化画布缩放比例
scale: 0.7,
allowDrop: true, // 允许从工具栏拖拽节点到画布
allowZoom: true, // 允许缩放
"draggingTool.dragsLink": true, // 拖拽节点时是否同时移动连线
"draggingTool.isGridSnapEnabled": true, // 拖拽节点时是否吸附到网格
"linkingTool.isUnconnectedLinkValid": true, // 允许创建未连接的连线
"linkingTool.portGravity": 20, // 连线吸附到节点端口的力度
"relinkingTool.isUnconnectedLinkValid": true, // 允许重新连接未连接的连线
"relinkingTool.portGravity": 20, // 重新连接连线时吸附到节点端口的力度
"relinkingTool.fromHandleArchetype": $(sx.Shape, "Diamond", {
segmentIndex: 0,
cursor: "pointer",
desiredSize: new sx.Size(8, 8),
fill: "tomato",
stroke: "darkred",
}), // 重新连接连线时起始点的手柄样式
"relinkingTool.toHandleArchetype": $(sx.Shape, "Diamond", {
segmentIndex: -1,
cursor: "pointer",
desiredSize: new sx.Size(8, 8),
fill: "darkred",
stroke: "tomato",
}), // 重新连接连线时结束点的手柄样式
"linkReshapingTool.handleArchetype": $(sx.Shape, "Diamond", {
desiredSize: new sx.Size(7, 7),
fill: "lightblue",
stroke: "deepskyblue",
}), // 调整连线形状时的手柄样式
rotatingTool: $(TopRotatingTool), // 自定义旋转工具
"rotatingTool.snapAngleMultiple": 15, // 旋转时对齐到的角度倍数
"rotatingTool.snapAngleEpsilon": 15, // 角度对齐的容差范围
"undoManager.isEnabled": true, // 启用撤销/重做功能
}
);

同步 gojs 中渲染的表格,也是 10px,现在考虑解决 x6 缩放问题

1
2
3
4
5
6
7
8
graph.resize(800, 600) // resize 画布大小
graph.translate(20, 20) // 在 x、y 方向上平移画布
graph.zoom(0.2) // 将画布缩放级别增加 0.2(默认为1)
graph.zoom(-0.2) // 将画布缩放级别减少 0.2
graph.zoomTo(1.2) // 将画布缩放级别设置为 1.2
// 将画布中元素缩小或者放大一定级别,让画布正好容纳所有元素,可以通过 maxScale 配置最大缩放级别
graph.zoomToFit({ maxScale: 1 })
graph.centerContent() // 将画布中元素居中展示

加入了 zoom 后,差不多比例了

接着研究线的坐标,发现是 base64 图片有偏移,保存时候添加了,解决了这个问题。又发现了新的问题,还是线的问题,图片与图片直连,两边都可以,就是旋转之后,连接线不匹配。研究线

常用api

https://x6.antv.antgroup.com/api/model/cell

节点/边渲染到画布后可以通过

cell.isNode()

cell.getZIndex()

cell.setZIndex(z: number) 来获取或设置 zIndex的值

cell.toFront()

cell.toBack() 来将其移到最顶层或最底层

cell.attrs 获取属性 要是 = 就是设置新的属性

cell.getAttrs() 获取属性

setAttrs(attrs: Attr.CellAttrs, options?: Cell.SetAttrOptions) 设置属性

默认深merge,值全部替换,如果原先有body,替换的没有,body也会消失

1
2
3
4
cell.setAttrs({    
body: { fill: '#f5f5f5' },
label: { text: 'My Label' },
})

浅的则body捕获消失,只替换替换的值

1
cell.setAttrs({ label: { text: 'My Label' } }, { deep: false })

replaceAttrs(…) 相当于刚刚上面那个身

updateAttrs(…) 相当于浅的

removeAttrs(…) 删除属性

cell.getAttrByPath() 路径为空时返回全部属性

cell.getAttrByPath(‘body’) 通过字符串路径获取属性值

还可以 [‘body’]

setAttrByPath(…) 根据属性路径设置属性值

cell.setAttrByPath(‘body’, { stroke: ‘#000000’ }) // 替换 body 属性值

cell.setAttrByPath(‘body/fill’, ‘#f5f5f5’) // 设置 body.fill 属性值浅保留原

cell.removeAttrByPath(‘body/fill’) 通过字符串路径删除属性值

attr(…)该方法是 getAttrByPathsetAttrByPathsetAttrs 三个方法的整合,提供了上面四种函数签名,是一个非常实用的方法。

cell.attr()

cell.attr(‘line/stroke’, ‘#c0c0c0’)

cell.attr(‘body/fill’)

1
2
3
4
5
6
7
8
9
10
11
const rect = new Shape.Rect({
x: 40,
y: 40,
width: 100,
height: 40,
data: {
bizID: 125,
date: '20200630',
price: 89.0,
},
})

data声明在和宽高同级别

cell.getData() 获取这个data里的对象

cell.setData() 设置 设置关联的数据,并触发 change:data 事件和画布重绘

cell.setData(data) 默认深merge

cell.setData(data, { overwrite: true }) 替换旧数据

cell.setData(data, { deep: false }) 浅

1
2
3
4
5
6
7
8
9
10
const obj = { name: 'x6', star: true }
node.setData(obj) // 此时会触发节点重绘

obj.star = false
node.setData(obj) // 注意,此时不会进行深比较,判定对象未发生修改,不会触发节点重绘

node.setData({
...obj,
star: false,
}) // 此时会触发节点重绘

replaceData(…) 用指定的数据替换原数据,相当于调用 setData(data, { ...options, overwrite: true })

updateData(…) 通过浅 merge 来更新数据,相当于调用 setData(data, { ...options, deep: false })

removeData(…) 删除数据。默认情况触发 change:data 事件和画布重绘,

getParent() …. 获取父节点。暂时没用到

节点属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const rect = new Shape.Rect({
x: 30,
y: 30,
width: 100,
height: 40,
attrs: {...},
data: {...},
zIndex: 10,
sale: {...},
product: {
id: '1234',
name: 'apple',
price: 3.99,
},
})

上面代码中的 attrsdatazIndex 都是标准的属性,其中 xy 是一对自定义选项,节点初始化时被转换为了 position 属性,同样 widthheight 也是一对自定义选项,节点初始化时被转换为了 size 属性,最后剩余的 saleproduct 两个对象是非标准的属性。

1
2
3
4
5
6
7
// 获取标准属性
const zIndex = node.getProp('zIndex')
const position = node.getProp('position')
node.getProp('size')

// 获取非标准属性
const product = node.getProp('product')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 设置单个属性:
node.setProp('size', { width: 100, height: 30 })
node.setProp('zIndex', 10)

// 同时设置多个属性
node.setProp({
size: {
width: 100,
height: 30,
},
zIndex: 10,
})


rect.removeProp('zIndex')
rect.removeProp('product/id')

prop()是上面的整合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 获取属性:
rect.prop()
rect.prop('zIndex')
rect.prop('product/price')

// 设置属性:
rect.prop('zIndex', 10)
rect.prop('product/price', 5.99)
rect.prop({
product: {
id: '234',
name: 'banana',
price: 3.99,
},
})