前言
研究了两周的时间基本上搞清楚了,x6 的来龙去脉,最起码能完成我项目中复杂的功能了,在写这篇博客之前,费了老鼻子劲了, 网上的资料呢又少之甚少,为了让博友们少走点弯路我把我这几天总结的心得在这里赘述一下。 我只是写了部分功能,如果还有一位可以私信我, 如果我会 我会直言不讳的耐心解答
创建画布
<template>
<div class="surface">
<div id="canvasPanel" /> // 画布
<div id="minimapContainer" /> // minimap
</div>
</template>
<script>
import { Graph, Shape } from '@antv/x6'
mounted () {
this.createGraphic()
},
methods: {
createGraphic () {
const container = document.getElementById('canvasPanel')
const minimapContainer = document.getElementById('minimapContainer')
const width = container.scrollWidth
const height = (container.scrollHeight || 500) - 20
this.graph = new Graph({
container: container,
width,
height,
// 这个非常非常重要,我在这个上边至少耽误了两天, 甚至因为这个差点放弃使用 x6
// 因为我在项目中 是要滚动条的,当点击之后 滚动条会回到顶部,加上这个的话 svg 不会重新排序
sorting: 'approx',
grid: true,
keyboard: true,
scroller: {
enabled: true,
pageVisible: false,
pageBreak: true,
pannable: true
},
minimap: {
enabled: true,
width: 200,
height: 160,
padding: 10,
container: minimapContainer
},
snapline: true, // 对齐线
connecting: {
// connectionPoint 和 anchor 也很重要 但是这个在文档中说的很明白了, 但是有一个坑一定注意x6 的版本,我就是因为版本不是最新的导致 port 不起作用, 这个配置是 node 和 port 同时起作用的
connectionPoint: {
name: 'anchor',
args: {
sticky: true
}
},
anchor: {
name: 'midSide',
args: {
direction: 'H'
}
},
snap: {
radius: 100
}, // snap 设置为 true 时连线的过程中距离节点或者连接桩 10px 时会触发自动吸附
allowBlank: false, // 是否允许连接到画布空白位置的点
allowLoop: false, // 是否允许创建循环连线,即边的起始节点和终止节点为同一节点
// 这个是拖拽的时候线的样式,可以在拖拽成功之后再修改线的样式
createEdge () {
return new Shape.Edge({
attrs: {
line: {
strokeDasharray: '5 5',
stroke: 'red',
strokeWidth: 2,
sourceMarker: {
tagName: 'circle',
r: 5,
cx: 5,
fill: 'red'
},
targetMarker: {
tagName: 'circle',
r: 5,
cx: 5,
fill: 'red'
}
}
}
})
}
// 可以写连线的验证
validateConnection ({ sourceView, targetView, sourceMagnet, targetMagnet}{}
})
// 这里我只做了连线成功的线样式的处理
this.graph.on('edge:connected', (args) => {
const edge = args.edge
const node = args.currentCell
const elem = args.currentMagnet
const portId = elem.getAttribute('port')
// 触发 port 重新渲染
node.setPortProp(portId, 'connected', true)
// 更新连线样式
edge.attr({
line: {
strokeDasharray: ''
}
})
})
this.graph.on('edge:removed', ({ cell, edge }) => {
})
// 双击的时候编辑视图
this.graph.on('node:dblclick', ({ cell, node }) => {
// 获取 node 中的 vue 模板数据
let currentNode = node.store.data.component.data().nodeData
// 不改变引用地址
Object.assign(this.currentNode, currentNode)
this.currentNode.drawer = true
})
// 添加刪除按鈕, 暂时没有找到删除按钮的回调和样式
this.graph.on('edge:mouseenter', ({ edge }) => {
edge.addTools([
{
name: 'button-remove',
args: {
distance: -30
}
}
])
})
// 刪除按鈕
this.graph.on('edge:mouseleave', ({ edge }) => {
// 可以只定删除某个 tool
edge.removeTools()
})
},
}
</script>
注册 node 节点
import baseNode from './node'
export default (Graph, node) => {
let [startIndex, itemHeight, titleHeight, viewCount] = [0, 25, 30, 3]
let customNode = Graph.addNode({
shape: 'vue-shape',
view: 'ball',
x: node.x,
y: node.y,
width: 300,
height: 105,
id: node.id,
// 这里 注意 一定要引入 import { VueShapeView } from '@antv/x6-vue-shape' 文档中没有,资料也搜不到
// 如果使用不到 VueShapeView 也可以 import '@antv/x6-vue-shape' 这样引入
component: {
template: `<base-node @loadport="loadPort" :node="nodeData"></base-node>`,
data () {
return {
nodeData: node
}
},
methods: {
loadPort (event) {
startIndex = Math.floor(event.srcElement.scrollTop / itemHeight)
let afterList = node.columns.slice(
startIndex,
startIndex + viewCount
)
customNode.removePorts({ silent: true })
const ports = getRenderPorts(afterList)
customNode.addPorts(ports)
}
},
components: {
baseNode
}
},
tools: [
{
name: 'button-remove',
args: {
attrs: {
fill: '#7c68fc',
stroke: '#333'
},
x: '100%',
y: 0,
offset: { x: -15, y: 14 }
}
}
],
ports: {
groups: {
in: {
markup: [
{
tagName: 'rect',
selector: 'rect'
}
],
attrs: {
rect: {
magnet: true,
stroke: '#31d0c6',
fill: 'rgba(255,255,255,0.1)',
strokeWidth: 0,
width: 294,
height: 25,
x: 0,
y: -12
}
},
position: {
name: 'absolute'
}
},
root: {
markup: [
{
tagName: 'rect',
selector: 'rect'
}
],
attrs: {
rect: {
stroke: '#31d0c6',
fill: 'rgba(255,255,255,0.1)',
strokeWidth: 0,
width: 300,
height: 30,
x: 0,
y: 0
}
},
position: {
name: 'absolute'
}
}
},
items: [
]
}
})
function getRenderPorts (visibleView) {
const offsetY = titleHeight + itemHeight / 2
let visiblePorts = []
for (let [index, column] of visibleView.entries()) {
visiblePorts.push({
id: column.nodeId,
group: 'in',
zIndex: 100,
args: {
x: 0,
y: index * itemHeight + offsetY
}
})
}
visiblePorts.unshift({
id: 'port-in-root',
group: 'root',
index: -1,
args: {
offset: Math.round(titleHeight / 2)
}
})
return visiblePorts
}
let defaultList = node.columns.slice(0, 3)
const ports = getRenderPorts(defaultList)
customNode.addPorts(ports)
}