Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
216 views
in Technique[技术] by (71.8m points)

javascript - D3 + React - The whole force graph is redrawn when updated data arrives instead of updating only links

I'm quite new in D3. I would like to draw a graph using the data received from JSON (the graph contains nodes connected with each other by links with a given percentage of occupancy). After the first query from the database, the graph looks correct. After a few seconds, I send another query (to update the data on the use of links) and unfortunately, instead of updating only the text (linkOccup), the whole graph is deleted and redrawn (with correct updated data). I would like only the information about occupancy of links to be updated instead of redrawing the entire graph. Does anyone know the solution to the problem?

import React, { useEffect, useRef } from 'react';
import useResizeObserver from './useResizeObserver';
import * as d3 from 'd3';
import { select } from 'd3';

function ForceGraph({ linksData, nodesData }) {
  const svgRef = useRef();
  const wrapperRef = useRef();
  const dimensions = useResizeObserver(wrapperRef);

  const links = linksData;
  const nodes = nodesData;

  useEffect(() => {
    const svg = select(svgRef.current);
    if (!dimensions) return;

    svg.attr('viewBox', [
      -dimensions.width / 2,
      -dimensions.height / 2,
      dimensions.width,
      dimensions.height,
    ]);

    const drag = simulation => {
      const dragstarted = d => {
        if (!d3.event.active) simulation.alphaTarget(0.3).restart();
        d.fx = d.x;
        d.fy = d.y;
      };

      const dragged = d => {
        d.fx = d3.event.x;
        d.fy = d3.event.y;
      };

      const dragended = d => {
        if (!d3.event.active) simulation.alphaTarget(0);
        d.fx = null;
        d.fy = null;
      };

      return d3
        .drag()
        .on('start', dragstarted)
        .on('drag', dragged)
        .on('end', dragended);
    };

    const simulation = d3
      .forceSimulation(nodes)
      .force(
        'link',
        d3.forceLink(links).id(d => d.dpid)
      )
      .force('charge', d3.forceManyBody().strength(-1200));

    const link = svg
      .selectAll('line')
      .data(links)
      .join(
        enter => enter.append('line').attr('class', 'new-element'),
        update => update.attr('class', 'updated-element')
      )
      .attr('stroke-width', 2)
      .attr('stroke', '#000')

      .call(drag(simulation));

    const node = svg
      .selectAll('circle')
      .data(nodes)
      .join(
        enter => enter.append('circle').attr('class', 'new-element'),
        update => update.attr('class', 'updated-element')
      )
      .attr('r', 18)
      .call(drag(simulation));

    const linkOccup = svg
      .selectAll('text')
      .data(links)
      .join(
        enter =>
          enter
            .append('text')
            .text(d => {
              if (typeof d.max_link_occupancy !== 'undefined') {
                return d.max_link_occupancy + '%';
              } else {
                return d.bw + ' Mb/s';
              }
            })
            .attr('class', 'new-element'),
        update =>
          update
            .text(d => {
              if (typeof d.max_link_occupancy !== 'undefined') {
                return d.max_link_occupancy + '%';
              } else {
                return d.bw + ' Mb/s';
              }
            })
            .attr('class', 'updated-element')
      )
      .attr('text-anchor', 'middle')
      .attr('dominant-baseline', 'central')
      .attr('font-size', 18)
      .call(drag(simulation));

    const label = svg
      .selectAll('.label')
      .data(nodes)
      .join(
        enter => enter.append('text').attr('class', 'new-element'),
        update => update.attr('class', 'updated-element')
      )
      .text(d => {
        return d.dpid;
      })
      .attr('class', 'label')
      .attr('text-anchor', 'middle')
      .attr('dominant-baseline', 'central')
      .attr('font-size', 18)
      .attr('fill', 'white');

    simulation.on('tick', () => {
      link
        .attr('x1', d => d.source.x)
        .attr('y1', d => d.source.y)
        .attr('x2', d => d.target.x)
        .attr('y2', d => d.target.y);

      node.attr('cx', d => d.x).attr('cy', d => d.y);

      label
        .attr('x', d => {
          return d.x;
        })
        .attr('y', d => {
          return d.y;
        });

      linkOccup
        .attr('x', d => (d.source.x + d.target.x) / 2)
        .attr('y', d => (d.source.y + d.target.y) / 2);
    });
  }, [linksData, dimensions]);

  return (
    <div className="rootNew" ref={wrapperRef} style={{ marginBottom: '2rem' }}>
      <svg className="svgNew" ref={svgRef}></svg>
    </div>
  );
}

export default ForceGraph;

question from:https://stackoverflow.com/questions/66066271/d3-react-the-whole-force-graph-is-redrawn-when-updated-data-arrives-instead

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)
Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...