Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/406.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如何在d3中正确实现force node链接图的更新模式?_Javascript_Reactjs_D3.js_Force Layout - Fatal编程技术网

Javascript 如何在d3中正确实现force node链接图的更新模式?

Javascript 如何在d3中正确实现force node链接图的更新模式?,javascript,reactjs,d3.js,force-layout,Javascript,Reactjs,D3.js,Force Layout,我在React应用程序中实现节点链接图的强制布局的更新模式时遇到问题。根据我的理解,模式是加入、退出、更新、输入,但显然我没有正确地实现它。下面是一个指向沙箱的链接,其中有一个复制问题的最小示例:一些链接消失,但没有新节点进入,也没有节点退出 基本上,每隔三秒钟,我就要交换数据源,并用适当的节点链接更新svg 下面是一个图表包装器,它允许我在React应用程序中编写纯d3: import React, { useEffect, useRef, useState } from 'react'; i

我在React应用程序中实现节点链接图的强制布局的更新模式时遇到问题。根据我的理解,模式是加入、退出、更新、输入,但显然我没有正确地实现它。下面是一个指向沙箱的链接,其中有一个复制问题的最小示例:一些链接消失,但没有新节点进入,也没有节点退出

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

下面是一个图表包装器,它允许我在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;
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.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;
    }

    //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" ? 30 : 20;
      })
      .attr("fill", (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", "node")
      .call(
        d3
          .drag()
          .on("start", dragstarted)
          .on("drag", dragged)
          .on("end", dragended)
      );

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

    //restart simulation
    vis.force
      .nodes(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)
      )
      .restart();
  }
}