creating new force graph with react+ts and d3

692 Views Asked by At

I am trying to create a simple force graph with React + TS and D3.

Main issue Connect my nodes with a line

Here is my code:

import React from "react";
import { forceSimulation, forceManyBody, forceLink, forceCenter } from 'd3-force'
import d3 ,{ SimulationNodeDatum } from "d3";
import { Box } from "@mui/system";


type Node = {
  id: string;
  class: string;
  x?: string;
  y?: string;
}

type Link = {
  source: string;
  target: string;
  connectionType: string;
}

The data (in the same file):

const { links, nodes } = {
  nodes: [{
    id: "honda",
    class: "cars",
  }, {
    id: "civic",
    class: "cars",
  },{
    id: "toyota",
    class: "cars",
  },{
    id: "corola",
    class: "cars",
  }],
  links: [{
    source: 'civic',
    target: 'honda',
    connectionType: 'm2o'
  },{
    source: 'corola',
    target: 'toyota',
    connectionType: 'm2o'
  }]
}

... and here's the component:

function Rd4Component (): JSX.Element {
  const simulation = forceSimulation(nodes as SimulationNodeDatum[]);
    simulation
      .force('charge', forceManyBody().strength(-100))
    .force('link',
      forceLink(links)
        .id((d) => (d as Node).id)
        .distance(75)
    )
      .force("center", forceCenter(300, 300));

  const svg = d3.select('#Target');

  const node = svg
      .selectAll("circle")
      .data(nodes)
      .enter()
      .append("circle")
      .attr("r", 15)
      .attr("stroke", "green")
      .attr("stroke-width", 0.5)
      .style("fill", "red");

  const link = svg
      .selectAll('path.link')
      .data(links)
      .enter()
      .append("path")
      .attr("stroke", "black")
      .style("fill", "none");


  function ticked() {
     link
       .attr("x1", function (d: any) {
         return d.source.x;
       })
       .attr("y1", function (d: any) {
         return d.source.y;
       })
       .attr("x2", function (d: any) {
         return d.target.x;
       })
       .attr("y2", function (d: any) {
         return d.target.y;
       });

    node
      .attr("cx", function (d: any) {
        return d.x;
      })
      .attr("cy", function (d: any) {
        return d.y;
      });

    // label
    //   .attr("x", function (d: any) {
    //     return d.x + 5;
    //   })
    //   .attr("y", function (d: any) {
    //     return d.y + 5;
    //   });
  }

  simulation.nodes(nodes as SimulationNodeDatum[])
    .on("tick", ticked)


  return <Box sx={{overflowY: "scroll"}}>
    <svg height={600} width={600} id="Target" />
  </Box>
}

export default Rd4Component;

Here are the results:

the render

inspected markup

1

There are 1 best solutions below

0
Michael Rovinsky On

Replace path with line (or specify a d attribute for <path> instead of x1, x2, y1, y1 used for a <line>)

const { links, nodes } = {
  nodes: [{
    id: "honda",
    class: "cars",
  }, {
    id: "civic",
    class: "cars",
  },{
    id: "toyota",
    class: "cars",
  },{
    id: "corola",
    class: "cars",
  }],
  links: [{
    source: 'civic',
    target: 'honda',
    connectionType: 'm2o'
  },{
    source: 'corola',
    target: 'toyota',
    connectionType: 'm2o'
  }]
}

const simulation = d3.forceSimulation(nodes);
    simulation
      .force('charge', d3.forceManyBody().strength(-50))
    .force('link',
      d3.forceLink(links)
        .id((d) => d.id)
        .distance(50)
    )
      .force("center", d3.forceCenter(100, 100));

  const svg = d3.select('svg');

  const link = svg
      .selectAll('line.link')
      .data(links)
      .enter()
      .append("line")
      .attr("stroke", "black")
      .style("fill", "none");
  
  const node = svg
      .selectAll("circle")
      .data(nodes)
      .enter()
      .append("circle")
      .attr("r", 15)
      .attr("stroke", "green")
      .attr("stroke-width", 0.5)
      .style("fill", "red");



  function ticked() {
     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;
       });

    node
      .attr("cx", function (d) {
        return d.x;
      })
      .attr("cy", function (d) {
        return d.y;
      });

    // label
    //   .attr("x", function (d: any) {
    //     return d.x + 5;
    //   })
    //   .attr("y", function (d: any) {
    //     return d.y + 5;
    //   });
  }

  simulation.nodes(nodes)
    .on("tick", ticked)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="200" height="200">
  
</svg>