antv g6使用
- 官方文檔
- 具體使用
-
- 業務需求
- 主要問題
- 完整代碼
官方文檔
官方文檔連結(看完之後的感覺是我懂了,但沒完全懂。)
具體使用
業務需求
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2csMHZtJGcoNDWoRnMMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLyEDN0MTO0kDM1AzNwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
- 做資料血緣,大概類似做ER圖吧,一開始想使用echarts拓撲圖來做,但是echarts有個定位問題一直沒弄明白,找不到辦法解決,echarts的節點需要自定義位置,但是我們是動态資料,且力引導算法不滿足業務需求,是以最後選擇了antv g6。
主要問題
1、需要自定義節點,g6使用自定義dom節點滿足改需求,但是根據文檔提供,dom節點無法使用g6提供的點選事件。
- 通過配置節點type,使節點指向某自定義節點。
// 目前節點高亮 通過type比對自定義節點
if (this.data.nodes[i].highLight == 1) {
this.data.nodes[i].type = 'center'
} else {
this.data.nodes[i].type = 'dom-node'
}
- 自定義dom節點
G6.registerNode(
'dom-node', {
draw: (cfg, group) => {
// console.log(cfg, group)
const shape = group.addShape('dom', {
attrs: {
width: cfg.size[0],
height: cfg.size[1],
// 傳入 DOM 的 html
html: `
<div onclick="select(${cfg.name})" id="${cfg.select}" class="dom-node-style" style="cursor:pointer; border-radius: 5px; width: ${
cfg.size[0] - 5
}px; height: ${cfg.size[1] - 5}px; display: flex;">
<span style="margin:auto; padding:auto; color: #000">${cfg.id}</span>
</div> `,
},
draggable: true,
});
return shape;
},
},
'single-node',
);
// 目前表節點
G6.registerNode(
'center', {
draw: (cfg, group) => {
// console.log(cfg)
const shape = group.addShape('dom', {
attrs: {
width: cfg.size[0],
height: cfg.size[1],
// 傳入 DOM 的 html ${cfg.isActive ? "class='selected-style'" : "class='node-style'"}"
html: `
<div onclick="select(${cfg.name})" id="${cfg.select}" class="node-style" style="width: ${
cfg.size[0] - 5
}px; height: ${cfg.size[1] - 5}px; display: flex;border-radius: 5px;cursor:pointer;">
<span style="margin:auto; padding:auto; color: #000">${cfg.id}</span>
</div> `,
},
draggable: true,
});
return shape;
},
},
'single-node',
);
2、調整狀态樣式,點選節點或線時改變樣式。
- 預設線可直接配置。具體可檢視文檔
defaultEdge: {
style: {
endArrow: true,
lineWidth: 2,
stroke: '#CED4D9',
fill: "#CED4D9",
},
},
edgeStateStyles: {
click: {
lineWidth: 2,
stroke: '#5394ef',
fill: "#5394ef",
},
},
// 邊點選事件
graph.on('edge:click', (e) => {
// 先将所有目前是 click 狀态的邊置為非 click 狀态
const clickEdges = graph.findAllByState('edge', 'click');
clickEdges.forEach((ce) => {
graph.setItemState(ce, 'click', false);
});
const item = e.item; // 擷取滑鼠進入的邊元素對象
const jobId = item._cfg.model.jobId
graph.setItemState(item, 'click', true); // 設定目前邊的 click 狀态為 true
that.getLineInfo(jobId)
});
- 自定義dom節點需要動态改變class屬性來改變樣式,暫未發現更好的解決辦法,歡迎交流!
// 節點點選事件
let that = this;
window.select = function(id) {
const clickEdges = graph.findAllByState('edge', 'click');
clickEdges.forEach((ce) => {
graph.setItemState(ce, 'click', false);
});
that.getNodeInfo(id)
var divId = 'temp' + id
// 擷取目前選中div 點選後該div替換選中樣式 其他所有div恢複未選中樣式
var selectId = document.getElementById(divId)
for (let i in that.data.nodes) {
if (that.data.nodes[i].name == id) {
// if (that.data.nodes[i].isActive == false) {
selectId.setAttribute('class', 'selected-style')
that.data.nodes[i].isActive = true
// }
} else {
let tempId = document.getElementById('temp' + that.data.nodes[i].name)
if (that.data.nodes[i].type == 'dom-node') {
tempId.setAttribute('class', 'dom-node-style')
} else {
tempId.setAttribute('class', 'node-style')
}
that.data.nodes[i].isActive = false
}
}
}
3、如何銷毀畫布?
- 在每次請求資料之前判斷,如果已經有資料則銷毀畫布。
// 避免多次渲染資料 銷毀畫布
if (this.chart !== '') {
this.chart.destroy()
}
- 在渲染g6前指派
this.chart = graph
4、互動模式使用,default 模式中包含點選選中節點行為和拖拽畫布行為,該行為觸發時會重新渲染畫布導緻自定義dom節點設定的狀态樣式取消。(自定義dom節點寫在draw中,改變dom節點的狀态樣式如選中後變色等,是通過動态改變class屬性實作的),
該問題目前沒有解決
。
5、布局問題,g6也使用自定義節點位置,但是提供了dagre層次布局,該算法可滿足需求。
layout: {
type: 'dagre', //層次布局
rankdir: 'LR', // 可選,預設為圖的中心
align: 'DL', // 可選
nodesep: 25, // 可選
ranksep: 25, // 可選
controlPoints: true, // 可選
},
完整代碼
<script>
//引入g6
import G6 from '@antv/g6';
import {
getGraphData,
getLineInfo,
getNodeInfo
} from '@/api/home/assetCatalogueDetail'
export default {
data() {
return {
id: 0,
type:0,//0-全部血緣,1-直系父子,2-所有父表,3-所有子表
activeName:'first',
chart: '',
visible: true,
nodeList: [],
lineList: [],
data: {
// 點集
nodes: [],
// 邊集
edges: [],
}
}
},
methods: {
init(id) {
this.id = id
this.getData()
},
getData() {
// 避免多次渲染資料 銷毀畫布
if (this.chart !== '') {
this.chart.destroy()
}
this.getNodeInfo(this.id)
getGraphData(Object.assign({
basicDataId:this.id,
relationType: this.type
})).then(response => {
this.data.nodes = response.data.data.node
this.data.edges = response.data.data.line
// console.log(this.data)
for (let i in this.data.nodes) {
// g6 id代表節點名稱
let name = this.data.nodes[i].name
let id = this.data.nodes[i].id
this.data.nodes[i].id = name
this.data.nodes[i].name = id
// 設定節點的連接配接點 anchorPoint 指的是邊連入節點的相對位置,即節點與其相關邊的交點位置
this.data.nodes[i].anchorPoints = [
[0.5, 0],
[1, 0.5],
[0, 0.5],
[0.5, 1],
]
this.data.nodes[i].select = 'temp' + id
this.data.nodes[i].isActive = false
this.data.nodes[i].size = [120, 40]
// 目前節點高亮 通過type比對自定義節點
if (this.data.nodes[i].highLight == 1) {
this.data.nodes[i].type = 'center'
} else {
this.data.nodes[i].type = 'dom-node'
}
}
this.renderView()
})
},
renderView() {
G6.registerNode(
'dom-node', {
draw: (cfg, group) => {
// console.log(cfg, group)
const shape = group.addShape('dom', {
attrs: {
width: cfg.size[0],
height: cfg.size[1],
// 傳入 DOM 的 html
html: `
<div onclick="select(${cfg.name})" id="${cfg.select}" class="dom-node-style" style="cursor:pointer; border-radius: 5px; width: ${
cfg.size[0] - 5
}px; height: ${cfg.size[1] - 5}px; display: flex;">
<span style="margin:auto; padding:auto; color: #000">${cfg.id}</span>
</div> `,
},
draggable: true,
});
return shape;
},
},
'single-node',
);
// 目前表節點
G6.registerNode(
'center', {
draw: (cfg, group) => {
// console.log(cfg)
const shape = group.addShape('dom', {
attrs: {
width: cfg.size[0],
height: cfg.size[1],
// 傳入 DOM 的 html ${cfg.isActive ? "class='selected-style'" : "class='node-style'"}"
html: `
<div onclick="select(${cfg.name})" id="${cfg.select}" class="node-style" style="width: ${
cfg.size[0] - 5
}px; height: ${cfg.size[1] - 5}px; display: flex;border-radius: 5px;cursor:pointer;">
<span style="margin:auto; padding:auto; color: #000">${cfg.id}</span>
</div> `,
},
draggable: true,
});
return shape;
},
},
'single-node',
);
const graph = new G6.Graph({
renderer: 'svg', //使用 Dom node 的時候需要使用 svg 的渲染形勢
container: 'mountNode',
width: 800,
height: 500,
layout: {
type: 'dagre', //層次布局
rankdir: 'LR', // 可選,預設為圖的中心
align: 'DL', // 可選
nodesep: 25, // 可選
ranksep: 25, // 可選
controlPoints: true, // 可選
},
defaultEdge: {
style: {
endArrow: true,
lineWidth: 2,
stroke: '#CED4D9',
fill: "#CED4D9",
// cursor:'pointer'
},
},
edgeStateStyles: {
click: {
lineWidth: 2,
stroke: '#5394ef',
fill: "#5394ef",
},
},
modes: {
default: [
// 'drag-canvas', //拖拽畫布
// 'zoom-canvas', //縮放畫布
]
},
fitCenter: true, //平移圖到中心将對齊到畫布中心,但不縮放
});
// 節點點選事件
let that = this;
window.select = function(id) {
const clickEdges = graph.findAllByState('edge', 'click');
clickEdges.forEach((ce) => {
graph.setItemState(ce, 'click', false);
});
that.getNodeInfo(id)
var divId = 'temp' + id
// 擷取目前選中div 點選後該div替換選中樣式 其他所有div恢複未選中樣式
var selectId = document.getElementById(divId)
for (let i in that.data.nodes) {
if (that.data.nodes[i].name == id) {
// if (that.data.nodes[i].isActive == false) {
selectId.setAttribute('class', 'selected-style')
that.data.nodes[i].isActive = true
// }
} else {
let tempId = document.getElementById('temp' + that.data.nodes[i].name)
if (that.data.nodes[i].type == 'dom-node') {
tempId.setAttribute('class', 'dom-node-style')
} else {
tempId.setAttribute('class', 'node-style')
}
that.data.nodes[i].isActive = false
}
}
}
// 邊點選事件
graph.on('edge:click', (e) => {
// 先将所有目前是 click 狀态的邊置為非 click 狀态
const clickEdges = graph.findAllByState('edge', 'click');
clickEdges.forEach((ce) => {
graph.setItemState(ce, 'click', false);
});
const item = e.item; // 擷取滑鼠進入的邊元素對象
const jobId = item._cfg.model.jobId
graph.setItemState(item, 'click', true); // 設定目前邊的 click 狀态為 true
that.getLineInfo(jobId)
});
this.chart = graph
graph.data(this.data); // 讀取 Step 2 中的資料源到圖上
graph.render(); // 渲染圖
// graph.fitView();
},
getNodeInfo(value) {
this.visible = true
getNodeInfo(Object.assign({
id: value,
curId: this.id
})).then(response => {
this.nodeList = response.data.data
// console.log(this.nodeList)
})
},
getLineInfo(value) {
this.visible = false
getLineInfo(value).then(response => {
this.lineList = response.data.data
// console.log(this.lineList)
})
},
}
}
</script>
<style>
.node-style {
background-color: #fff;
border: 1px solid #5B8FF9;
}
.selected-style {
background-color: orange;
}
.dom-node-style {
background-color: #fff;
border: 1px solid #000;
}
</style>