D3 v4: update strength setting

Mike Bostock has an example regarding updating the layout of the force. The example is based on v3 - how can you reproduce the same functionality in v4?

Here is my (pathetic) attempt .

I read the changes to select from the v4 change list, but the merge call is still confusing. In particular, it is not clear to me how the data connection interacts with the modeling calls nodes() and links() .

+8
force-layout
source share
1 answer

 var width = 300, height = 200; var color = d3.scaleOrdinal(d3.schemeCategory20); var nodes = [], links = []; var simulation = d3.forceSimulation() .force("link", d3.forceLink().id(function(d) { return d.id; })) .force("charge", d3.forceManyBody()) .force("center", d3.forceCenter(width / 2, height / 2)); var svg = d3.select("svg"); var linkLayer = svg.append('g').attr('id','link-layer'); var nodeLayer = svg.append('g').attr('id','node-layer'); // 1. Add three nodes and three links. setTimeout(function() { var a = {id: "a"}, b = {id: "b"}, c = {id: "c"}; nodes.push(a, b, c); links.push({source: a, target: b}, {source: a, target: c}, {source: b, target: c}); start(); }, 0); // 2. Remove node B and associated links. setTimeout(function() { nodes.splice(1, 1); // remove b links.shift(); // remove ab links.pop(); // remove bc start(); }, 2000); // Add node B back. setTimeout(function() { var a = nodes[0], b = {id: "b"}, c = nodes[1]; nodes.push(b); links.push({source: a, target: b}, {source: b, target: c}); start(); }, 4000); function start() { var link = linkLayer.selectAll(".link") .data(links, function(d) { return d.source.id + "-" + d.target.id; }); link.enter().append("line") .attr("class", "link"); link.exit().remove(); var node = nodeLayer.selectAll(".node") .data(nodes, function(d) { return d.id;}); node.enter().append("circle") .attr("class", function(d) { return "node " + d.id; }) .attr("r", 8); node.exit().remove(); simulation .nodes(nodes) .on("tick", tick); simulation.force("link") .links(links); } function tick() { nodeLayer.selectAll('.node').attr("cx", function(d) { return dx; }) .attr("cy", function(d) { return dy; }) linkLayer.selectAll('.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; }); } 
 .link { stroke: #000; stroke-width: 1.5px; } .node { fill: #000; stroke: #fff; stroke-width: 1.5px; } .node.a { fill: #1f77b4; } .node.b { fill: #ff7f0e; } .node.c { fill: #2ca02c; } 
 <script src="https://d3js.org/d3.v4.min.js"></script> <svg width="300px" height="200px"></svg> 

This way you really don't need d3-selection-merge for your example to work. The reason is because your node positions and links are updated by simulation. Thus, you will want to add nodes and links at startup, but any position updates will occur at the end of the launch method when the simulation starts.

One of the main drawbacks of your source code was that you called svg.selectAll ('. Node') and svg.selectAll ('. Link') in the initial stages of the script. When you do this, there are no nodes or links related to svg, so you get a choice of d3 with empty DOM elements. This is great if you want to add elements with enter (). Append () - however, when deleting items, this will not work. Using the same, deprecated d3 choice to remove items from exit (). Remove () does not work b / c, there are no DOM elements in removing d3 to remove. You need to call svg.selectAll () every time to get the DOM elements that are currently in svg.

I also made a few minor changes to your code. Usually you need to always show links below nodes. When you add elements to the SVG, the most recently added nodes are placed at the top. However, if you add groups before hand (as was the case with linkLayer and nodeLayer in the code), any new added links will appear below any elements in the nodeLayer group.

0
source share

All Articles