d3 force copying instead of animating

Hi. I’m trying to make it so that when you click on a node, the graph updates to show nodes around it. How do I get this to animate instead of copy?:

let width = 800;
let height = 600;

const svg = d3.select("body")
  .append("svg")
  .attr("width", width)
  .attr("height", height)
  .attr("style", "border:1px solid #000")
  .attr("viewBox", [0, 0, width, height]);

let link = svg.append("g")
        .attr("stroke", "#000")
        .attr("stroke-width", 1.5)
      .selectAll("line"),
    link1 = link.join("line");

let node = svg.append("g")
    .attr("stroke", "#fff")
    .attr("stroke-width", 1.5)
      .selectAll("g"),
    node1 = node
      .enter()
      .append("g");  // IMP for transform of Labels

let ticked = function() {
  node1.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

  link1.attr("x1", d => d.source.x)
      .attr("y1", d => d.source.y)
      .attr("x2", d => d.target.x)
      .attr("y2", d => d.target.y);
}

const simulation = d3.forceSimulation()
    .force("charge", d3.forceManyBody()/*.strength(-1000)*/)
    .force("center", d3.forceCenter(width / 2, height / 2))
    .force("link", d3.forceLink().id(d => d.tag).distance(200))
    .on("tick", ticked);

// TEST
const n1 = {tag: 'Tag 123'},
      n2 = {tag: 'tag 4567'},
      n3 = {tag: 'tag 890'};

Object.assign(svg.node(), {update(data) {
  node = node.data(data.nodes, d => d.tag);
  // node.exit().remove();
  node1 = node
    .enter()
    .append("g");  // IMP for transform of Labels
  node1.append('circle')
          .attr("r", 5)
          .attr("fill", "#1f77b4");
  node1
    .attr('stroke', '#000')
    .append("text")
      .text(d => d.tag)
      .attr('dx', 5).attr('dy', -5)
      .attr('style', 'cursor:pointer;')
      .on('click', (d) => {
        // TEST. should be nodes around d.
        let data = {
              nodes:[n2, n3],
              connections:[
                {source: n2, target: n3}
              ]
            };
        svg.node().update(data);
      });
  // node = node.merge(node1);

  link = link.data(data.connections);
  link1 = link.join("line");

  simulation.nodes(data.nodes);
  simulation.force("link").links(data.connections);
  simulation.alpha(1).restart();
}});

let data = {
      nodes:[n1, n2],
      connections:[
        {source:n1, target:n2}
      ]
    };
svg.node().update(data);

I figured it out:

  • the format of my test links were wrong – source & target were supposed to be id’s
  • data should be probably be copied
  • use of node1, link1 was non-standard and might have confused things
  • do use .join(). When passing in a function, the arg is the predecessor, and the function should return the entered element.
1 Like