antv-G6拓扑图的自定义关系图小结
一、需求背景
结合项目需求有如下需求:
- 分为上下两行,类似关系图;
- 自定义node样式;
- 击一个node,再点击另一类别node,生成连线。点击一个node后,再次点击当前node或者点击其他位置,取消选中。直线连接。点击连线,可以删除连线。连线样式有弧度。划上node节点出现弹框
二、主要代码实现
import G6 from '@antv/g6'; //引入G6
const setG6Topu = ()=>{
//先清除原有
if(graphs.current){
graphs.current.destroy()
}
graphs.current = new G6.Graph({
container: "topologicalCon",
width: tuopuWidth,
height: 154,
linkCenter: true,
modes: {
// 根据不同需求设置拓扑图展示/可操作状态
default: ['click-add-edge','rectNode'],
view: [],
},
defaultNode: {
type: "rectNode",
size: [32,48],
color: "#008dff",
style: {
fill: "#9EC9FF",
// lineWidth: 3
},
labelCfg: {
}
},
nodeStateStyles: {
selected: {
stroke: '#f00',
lineWidth: 5
}
},
plugins:[tooltip],
defaultEdge: {
type: 'polyline',
style: {
radius: 10,
offset: 12,
stroke: '#40A9FF',
lineWidth: 1,
}
}
});
//添加数据
graphs.current.data(tpData);
//切换状态
if(type=="view" || disableState){
graphs.current.setMode('view')
}else{
graphs.current.setMode('default')
}
//加载
graphs.current.render();
}
//self为自定义暂存点数据,getEdgesObj()/getNodeObj()/getEdgesObj等为自定义获取数据方法以及自定义判断
//G6自带核心方法黄色框中标出。
//点击node逻辑:不可连线则清除,可连线则起点保存,终点生成线
//鼠标滑过逻辑:有起点下生成跟随鼠标的直需线
//点击线逻辑:有起点下点击则清空,无起点则为单纯点击线(本次需求为删除)
G6.registerBehavior('click-add-edge', {
// Set the events and the corresponding responsing function for this behavior
getEvents() {
return {
// 'click': 'onClick'
'node:click': 'onNodeClick', // The event is canvas:click, the responsing function is onClick
'mousemove': 'onMousemove', // The event is mousemove, the responsing function is onMousemove
'edge:click': 'onEdgeClick', // The event is edge:click, the responsing function is onEdgeClick
};
},
onNodeClick(ev) {
//点击node操作
const node = ev.item;
const point = { x: ev.x, y: ev.y };
const model = node.getModel();
let obg = {...tpData}
let {nodes=[],edges = []} = obg
//判断是否已被连线,有则该次清空连线事件
const hasLined = getEdgesObj(edges,model.id)
if(hasLined){
if (self.current.addingEdge && self.current.edge) {
graphs.current.removeItem(self.current.edge);
self.current.addingEdge = false
self.current.edge = null
self.current.id = null
return
}
return
}
//判断是否已有暂存点,有则为生成线,无则为生成新连线,做起点暂存
if (self.current.addingEdge && self.current.edge) {
const sourceObj = getNodeObj(nodes,self.current.id)
const targetObj = getNodeObj(nodes,model.id)
//判断起点终点为同一个
if(sourceObj.addtype == targetObj.addtype){
graphs.current.removeItem(self.current.edge);
self.current.addingEdge = false
self.current.edge = null
self.current.id = null
return
}
const lineWired = getEdgesObj(edges,self.current.id,model.id)
if(lineWired){
graphs.current.removeItem(self.current.edge);
self.current.addingEdge = false
self.current.edge = null
self.current.id = null
return
}
edges.push({
source: self.current.id,
target: model.id,
})
obg.edges = edges
seTtpData(obg)
self.current.addingEdge = false
self.current.edge = null
self.current.id = null
} else {
self.current.addingEdge = true
//添加线
self.current.edge = graphs.current.addItem('edge', {
source: model.id,
target: model.id,
})
self.current.id = model.id
}
},
onMousemove(ev) {
//鼠标在拓扑区域滑动操作
const point = { x: ev.x, y: ev.y };
if (self.current.addingEdge && self.current.edge) {
//有暂存点则修未连接线颜色/样式
graphs.current.updateItem(self.current.edge, {
type: 'line',
target: point,
style: {
lineDash: [2, 2] , // 虚线边
stroke: '#5FB333',
}
});
}
},
onEdgeClick(ev) {
//点击线触发事件
const currentEdge = ev.item;
if (self.current.addingEdge && self.current.edge === currentEdge) {
//有暂存点则进行删除暂存操作
graphs.current.removeItem(self.current.edge);
self.current.addingEdge = false
self.current.edge = null
self.current.id = null
}else{
//没暂存点则删除线
const node = ev.item
const model = node.getModel();
let obg = {...tpData}
let {edges = []} = obg
const sourceObj = getNodeObj(obg.nodes,model.source)
const targetObj = getNodeObj(obg.nodes,model.target)
let list = []
edges.forEach(el=>{
if(el.source == model.source && el.target == model.target){
return
}else{
list.push(el)
}
})
obg.edges = [...list]
//自定义node节点,网上流传两种方式,此种支持G6定义事件,符合实际需求
G6.registerNode('rectNode', {
draw: (cfg, group) => {
//根据条件判断可点击/不可点击样式,可不关注
let styObg = {
opacity:1,
fill:'#FFFFFF',
stroke:'#40A9FF',
color:'#000000',
textX:0,
textY:cfg.addtype != 'top'?-8:15,
imgX:-8,
imgY:cfg.addtype != 'top'?2:-20,
}
if(disableState || cfg.disabledFlag){
styObg={
opacity:0.3,
fill:'#FFFFFF',
stroke:'rgba(0,0,0,.25)',
color:'rgba(0,0,0,.25)',
textX:0,
textY:cfg.addtype != 'top'?-8:15,
imgX:-8,
imgY:cfg.addtype != 'top'?2:-20,
}
}
//最外面的那层
const shape = group.addShape('rect', {
draggable: true,
attrs: {
x: -16,
y: -24,
width: 32,
height: 48,
fill: styObg.fill, //填充色
stroke: styObg.stroke, //边框
radius: 8,
},
});
//里面的那层
group.addShape('image', {
attrs: {
x: styObg.imgX,
y:styObg.imgY,
width: 16,
height: 16,
img: cfg.img,
opacity: styObg.opacity,
},
name: 'ip-cp-icon',
});
//文字
group.addShape('text', {
attrs: {
textAlign: 'center',
x: styObg.textX,
y: styObg.textY,
width: 32,
height: 12,
lineHeight: 10,
fontSize:12,
text: getLable(cfg.label),
fill: styObg.color,
},
});
return shape;
},
});
//G6悬浮框
const tooltip = new G6.Tooltip({
getContent(e) {
const outDiv = document.createElement('div');
outDiv.style.minWidth = '180px';
outDiv.style.maxWidth = '680px';
let lineObj = getLineObj(e.item.getModel().id)
outDiv.innerHTML = `
<h4>${e.item.getModel().CiName || e.item.getModel().id}</h4>
<div>${相关连线'}:${lineObj.source?lineObj.source+' ——'+ lineObj.target :'-'} </div>`
return outDiv
},
itemTypes: ['node']
})
三、其他补充
node位置:在node的数据中的x,y属性定义,间隔与高度可动态计算;
容易忽略的一点:代码整理删除和修改地方可能有点乱,实现逻辑与思路看下就行;
四、成品
![]()
![]()
五、
其实,到第四个就完了,占占篇幅而已。

浙公网安备 33010602011771号