如何动态更新此D3JS力图

时间:2020-09-11 08:20:00

标签: d3.js

我从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);
}

1 个答案:

答案 0 :(得分:1)

在D3中,数据绑定到选择。您可以使用data()函数进行绑定。 如果要更新选择,请采用现有选择并再次绑定数据。这将创建一个新选择,并且该新选择的enterupdateexit选择将包含更改的元素。

例如,更改数据:

// 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()
// }

有关更深入的信息,check out the selection.join() tutorial.