1. 程式人生 > 實用技巧 >仿Neo4j裡的知識圖譜,利用d3+vue開發的一個網路拓撲圖

仿Neo4j裡的知識圖譜,利用d3+vue開發的一個網路拓撲圖

專案需要畫一個類似知識圖譜的節點關係圖。


  一開始用的是echart畫的。

  根據https://gallery.echartsjs.com/editor.html?c=xH1Rkt3hkb,成功畫出簡單的節點關係。

  如圖:

  總結——

    【優點】:關係一目瞭然,可以滑鼠懸浮檢視相鄰節點,其他節點淡化。

    【缺點】:拖動結果不理想,尤其是資料過多時,一旦拖動一個,整個頁面所有的節點都在動,很久都無法停止(可能是我配置方法不對,但是後續沒找到解決方法)


  於是轉而使用d3力導圖。

  除了基本的節點展示和拖動之外,還可以雙擊新增節點,以及右擊展示節點詳情。

  核心操作有以下:

    1、繪製graph力

var simulation = d3
.forceSimulation(nodes)
.force(
'collide',
d3
.forceCollide()
.radius(() => 30)
.iterations(2)
)
.force(
'charge',
d3
.forceManyBody()
// .distanceMax(300)
.strength(-400)
)
.force(
'link',
d3
.forceLink(links)
.id(d => d.id)
.distance(100)
)
.force('center', d3.forceCenter(this.width / 2, this.height / 2))
// .force('x', d3.forceX(this.width / 2))
// .force('y', d3.forceY(this.height / 2))

    2、繪製存放節點和關係的svg

var svgArea = d3
.select('.containers')
.append('svg')
.attr('viewBox', [0, 0, this.width, this.height])
.attr('class', 'd3Test')
.call(
d3.zoom().on('zoom', function() {
g.attr('transform', d3.event.transform)
})
)
.on('dblclick.zoom', () => {}) // 禁止雙擊放大
const g = this.svgArea.append('g').attr('class', 'content')

      3、繪製節點關係

var links = g
.append('g')
.attr('class', 'links')
.selectAll('path')
.data(links, function(d) {
if (typeof d.source === 'object') {
return d.source.name + '_' + d.relationship + '_' + d.target.name
} else {
return d.source + '_' + d.relationship + '_' + d.target
}
})
.join('path')
.attr('stroke-width', d => Math.sqrt(d.value))
.attr('class', 'link')
.attr('id', function(d) {
if (typeof d.source === 'object') {
return d.source.name + '_' + d.relationship + '_' + d.target.name
} else {
return d.source + '_' + d.relationship + '_' + d.target
}
})

      4、繪製節點

var nodes = g
.append('g')
.attr('class', 'nodes')
.selectAll('circle')
.data(nodes, d => d.name)
.join('circle')
.attr('r', d => (d.number ? d.number : 20))
.attr('class', 'node')
.attr('stroke', '#fff')
.attr('stroke-width', 1.5)
.attr('fill', this.color)
.on('dblclick', this.dbclickNode)//雙擊節點事件
.on('click', this.clickNode)//單擊節點觸發事件
// .on('mouseover', this.mouseoverNode)
// .on('mouseout', this.mouseoutNode)
.call(this.drag(this.simulation))
nodes.append('title').text(d => d.name)

      5、然後還有個讓節點緩慢停止下來的tick

 this.simulation.on('tick', () => {
this.links.attr('d', function(d) {
if (d.source.x < d.target.x) {
return (
'M ' +
d.source.x +
' ' +
d.source.y +
' L ' +
d.target.x +
' ' +
d.target.y
)
} else {
return (
'M ' +
d.target.x +
' ' +
d.target.y +
' L ' +
d.source.x +
' ' +
d.source.y
)
}
}) this.nodes
.attr('cx', function(d) {
if (d.fixed) {
d.fx = nodes[d.index].x
}
return d.x
})
.attr('cy', function(d) {
if (d.fixed) {
d.fy = nodes[d.index].y
}
return d.y
}) this.nodesName.attr('x', d => d.x).attr('y', d => d.y)
})

  附上官網案例:https://observablehq.com/@d3/force-directed-graph

  這個案例的版本好像比較老,個人建議用新版,不過新版的API有改動。

  參考案例:https://eisman.github.io/neo4jd3/