我从D3JS https://observablehq.com/@d3/force-directed-graph改编了这个力导向图示例。我正在使用VueJS调用代码,因此已将其实现为类。
初始图工作正常,但是当我尝试添加新数据时,它没有响应。据我所知,只要我可以访问D3边和顶点选择,我就应该能够随时调用它们的join()
函数,输入新数据,并且应该自动合并它们。也许我弄错了?
下面的代码相当长,但是只有两种值得注意的方法。首先,实例化D3Diagram对象后,我将调用initialise()
,它具有一系列边和顶点。稍后,当我想添加更多数据时,我使用新数组调用addMoreData()
方法。
import * as d3 from "d3";
export default class D3Diagram {
initialise(vertices, edges) {
this.vertices = vertices;
this.edges = edges;
this.createSvg();
this.createSimulation();
this.createEdgeSelection();
this.createVertexSelection();
this.configureSimulation();
}
createSvg() {
this.svg = d3.select('#diagram-wrapper')
.append('svg')
.attr('viewBox', [0, 0, 100, 100])
.classed('flex-grow-1', true);
}
createSimulation() {
this.simulation = d3.forceSimulation(this.vertices)
.force("link", d3.forceLink(this.edges).id(d => d.id))
.force("charge", d3.forceManyBody().strength(d => -4))
.force("center", d3.forceCenter(50, 50));
}
createEdgeSelection() {
this.edgeSelection = this.svg.append("g")
.attr("stroke", "#999")
.attr("stroke-opacity", 0.6)
.selectAll("line")
.data(this.edges)
.join("line");
this.edgeSelection.append("title").text(d => d.id);
}
createVertexSelection() {
this.vertexSelection = this.svg.append("g")
.attr("stroke", "#fff")
.attr("stroke-width", 0.5)
.selectAll("circle")
.data(this.vertices)
.join("circle")
.attr("r", 2)
.attr("fill", color)
.call(drag(this.simulation));
}
configureSimulation() {
this.simulation.on('tick', () => {
this.edgeSelection
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);
this.vertexSelection
.attr("cx", d => d.x)
.attr("cy", d => d.y);
});
}
addMoreData(vertices, edges) {
this.vertexSelection.join(vertices);
this.edgeSelection.join(edges);
}
}
function color() {
const scale = d3.scaleOrdinal(d3.schemeCategory10);
return d => scale(d.group);
}
function drag(simulation) {
function dragstarted(event) {
if (!event.active) simulation.alphaTarget(0.3).restart();
event.subject.fx = event.subject.x;
event.subject.fy = event.subject.y;
}
function dragged(event) {
event.subject.fx = event.x;
event.subject.fy = event.y;
}
function dragended(event) {
if (!event.active) simulation.alphaTarget(0);
event.subject.fx = null;
event.subject.fy = null;
}
return d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
}
答案 0 :(得分:1)
在D3中,数据绑定到选择。您可以使用data()
函数进行绑定。
如果要更新选择,请采用现有选择并再次绑定数据。这将创建一个新选择,并且该新选择的enter
,update
和exit
选择将包含更改的元素。
例如,更改数据:
// Initial, empty selection.
this.vertexSelection = this.svg.append("g")
.attr("stroke", "#fff")
.attr("stroke-width", 0.5)
// Bind the data once.
this.vertexSelection.data(data)
// Data join, creating new elements.
this.vertexSelection.join("line")
// Bind the data again.
this.vertexSelection.data(data)
// Data join. Will update, removing old data, appending new data,
// updating existing data.
this.vertexSelection.join("line")
您会注意到,根据设计,更新数据时的调用始终是相同的,只是用于初始化选择。因此,我们可以将所有这些放在幂等的render()
方法中:不管这是第一个调用还是第二个,第三个调用等等,它都将起作用。
然后,您可以像在当前代码中一样进行拆分。您可以在组件安装时调用init()
和render()
进行第一次渲染,并在有新数据时调用render()
。
// pseudocode
function init() {
// Initial, empty selection.
this.vertexSelection = this.svg.append("g")
.attr("stroke", "#fff")
.attr("stroke-width", 0.5)
}
function render(data) {
// Bind new data and do the data join.
this.vertexSelection
.data(this.data.vertexes)
.join("line")
}
mounted() {
this.init()
this.render()
}
// on data change... {
this.render()
// }