您如何在d3中正确实现强制节点链接图的更新模式?

问题描述

我无法在React应用程序中为节点链接图的强制布局实现更新模式。据我了解,该模式为join,exit,update,enter,但显然我没有正确实现它。以下是指向沙盒的链接,其中包含一个复制问题的最小示例https://codesandbox.io/s/sharp-moser-ubizi?file=/src/Chart.jsx:0-4867一些链接消失了,但是没有新节点进入,也没有节点退出

基本上,每三秒钟,我要交换数据源并使用适当的节点链接更新svg。

这里是Chart Wrapper,它允许我在React应用程序中编写纯d3:

import React,{ useEffect,useRef,useState } from 'react';
import data from "./products";
import data2 from "./products2";
import Chart from './Chart';
import * as d3 from 'd3';

const ChartWrapper = () => {
    const chartArea = useRef(null);
    const [chart,setChart] = useState(null);
    const [nodes,setNodes] = useState(data);
    let flag = true;

    useEffect(() => {
        if (!chart)
            setChart(new Chart(chartArea.current,nodes))
        else {
            chart.update(nodes);
        }
    },[nodes]);

    d3.interval(() => {
        let newNodes = flag ? data : data2
        setNodes(newNodes);
        flag = !flag
    },3000)

    return (<div className="chart-area" ref={chartArea}></div>)

}

export default ChartWrapper;

这是Chart class,其中包含构造函数update() function

import * as d3 from "d3";

import "./Chart.css";

const MARGIN = { TOP: 20,RIGHT: 20,BottOM: 20,LEFT: 20 };
const WIDTH = 400 - MARGIN.LEFT - MARGIN.RIGHT;
const HEIGHT = 400 - MARGIN.TOP - MARGIN.BottOM;
const AA_RED = "#cb3327";
const AA_BLUE = "#184485";

export default class Chart {
  constructor(chartarea,data) {
    const vis = this;
    vis.data = data;
    vis.svg = d3
      .select(chartarea)
      .append("svg")
      .attr("width",WIDTH + MARGIN.LEFT + MARGIN.RIGHT)
      .attr("height",HEIGHT + MARGIN.BottOM + MARGIN.RIGHT)
      .append("g")
      .attr("transfrom",`translate(${MARGIN.LEFT},${MARGIN.RIGHT})`);

    vis.link = vis.svg
      .selectAll(".link")
      .data(vis.data.links)
      .enter()
      .append("line")
      .attr("class","link");

    vis.force = d3
      .forceSimulation(vis.data.nodes)
      .force(
        "link",d3
          .forceLink()
          .id(function (d) {
            return d.name;
          })
          .strength(0.4)
          .links(vis.data.links)
      )
      .force("charge",d3.forceManyBody().strength(-500))
      .force("x",d3.forceX())
      .force("y",d3.forceY())
      .force("center",d3.forceCenter(WIDTH / 2,HEIGHT / 2))
      .force(
        "collision",d3
          .forceCollide()
          .radius((node) => {
            return 50;
          })
          .iterations(3)
      )
      .on("tick",function () {
        vis.link
          .attr("x1",function (d) {
            return d.source.x;
          })
          .attr("y1",function (d) {
            return d.source.y;
          })
          .attr("x2",function (d) {
            return d.target.x;
          })
          .attr("y2",function (d) {
            return d.target.y;
          });
        vis.node.attr("transform",function (d) {
          return "translate(" + d.x + "," + d.y + ")";
        });
      });

    vis.node = vis.svg
      .selectAll(".node")
      .data(vis.data.nodes)
      .enter()
      .append("g")
      .attr("class","node")
      .call(
        d3
          .drag()
          .on("start",dragstarted)
          .on("drag",dragged)
          .on("end",dragended)
      );
    vis.node
      .append("circle")
      .attr("r",(d) => {
        return d.type === "application" ? 30 : 20;
      })
      .attr("fill",(d) => {
        return d.type === "application" ? AA_RED : AA_BLUE;
      });

    vis.node
      .append("text")
      .attr("dx",(d) => {
        return d.type === "application" ? -8 : 24;
      })
      .attr("dy",".35em")
      .text(function (d) {
        return d.name;
      });

    function dragstarted(event,d) {
      if (!event.active) vis.force.alphaTarget(0.3).restart();
      d.fx = d.x;
      d.fy = d.y;
    }

    function dragged(event,d) {
      d.fx = event.x;
      d.fy = event.y;
    }

    function dragended(event,d) {
      if (!event.active) vis.force.alphaTarget(0);
      d.fx = null;
      d.fy = null;
    }
  }

  update(data) {
    const vis = this;
    vis.data = data;

    function dragstarted(event,d) {
      if (!event.active) vis.force.alphaTarget(0);
      d.fx = null;
      d.fy = null;
    }

    //join
    vis.node = vis.svg.selectAll(".node").data(vis.data.nodes);

    vis.link = vis.svg.selectAll(".link").data(vis.data.links);

    //exit
    vis.node.exit().remove();
    vis.link.exit().remove();

    //update
    vis.node
      .append("circle")
      .attr("r",(d) => {
        return d.type === "application" ? AA_RED : AA_BLUE;
      });

    vis.link.append("line").attr("class","link");

    //enter
    vis.node = vis.node
      .enter()
      .append("g")
      .attr("class",dragended)
      );

    vis.link = vis.link.enter().append("line").attr("class","link");

    //restart simulation
    vis.force
      .nodes(vis.data.nodes)
      .force(
        "link",d3
          .forceCollide()
          .radius((node) => {
            return 50;
          })
          .iterations(3)
      )
      .restart();
  }
}

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)