This example demonstrates how to add and remove nodes and links from a force-directed layout. The graph initially appears with three disconnected nodes A, B and C. After one second, the three are connected in a loop. At two seconds, node C is removed, along with the links A-C and B-C. At three seconds, node C is reintroduced, restoring the original links A-C and B-C. Every subsequent second alternates between these two steps.
This example uses the general update pattern for data joins. See also modifying a force layout with transitions.
forked from dbravin's block: Modifying a Force Layout
xxxxxxxxxx
<button id="reset" onclick="reset()">reset</button>
<button id="prev" onclick="prev()">prev</button>
<button id="next" onclick="next()">next</button>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
color = d3.scaleOrdinal(d3.schemeCategory10);
var a = {id: "a"},
b = {id: "b"},
c = {id: "c"}
var data = [
{
nodes: [],
links: []
},
{
nodes: [a],
links: []
},
{
nodes: [a,b],
links: [{source: a, target: b}]
},
{
nodes: [a,b,c],
links: [{source: a, target: b},{source: b, target: c},{source: c, target: a}]
},
{
nodes: [a,b],
links: [{source: a, target: b}]
},
{
nodes: [a],
links: []
},
{
nodes: [],
links: []
},
]
/*
var a1={id:'a1', key: 'prop1', value: 'value1'}
var data1 = [
{
nodes: [a, b],
links: [{source: a, target: b}]
},
{
nodes: [a,b,a1],
links: [{source: a, target: b},{source: a1, target: a}]
},
{
nodes: [a,b],
links: [{source: a, target: b}]
}
]
data = data1
*/
var simulation = d3.forceSimulation(data[0].nodes)
.force("charge", d3.forceManyBody().strength(-1000))
.force("link", d3.forceLink(data[0].links)
.distance(200))
//.distance(function(e){return e.source.key || e.target.key ? 100 : 200}))
.force("x", d3.forceX())
.force("y", d3.forceY())
.alphaTarget(1)
.on("tick", ticked);
var g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"),
link = g.append("g").attr("stroke", "#000").attr("stroke-width", 1.5).selectAll(".link"),
node = g.append("g").attr("stroke", "#fff").attr("stroke-width", 1.5).selectAll(".node");
var i=1
/*
var interval_id = setInterval(function(){
console.log(i,data, data[i])
if (i>=data.length-1){
clearInterval(interval_id)
}
draw(data[i++])
}, 3000)
*/
function reset(){
i=0
next()
}
function prev(){
if(i===0){
draw(data[i])
}
if (i>0){
draw(data[i--])
}
}
function next(){
if (i<data.length){
draw(data[i++])
}
}
function draw(data) {
// Apply the general update pattern to the nodes.
node = node.data(data.nodes, function(d) { return d.id;});
node.exit().remove();
node = node.enter().append("circle").attr("fill", function(d) { return color(d.id); })
.attr("r", 25)
//.attr("r", function(e){return e.key ? 5 : 25})
.merge(node);
// Apply the general update pattern to the links.
link = link.data(data.links, function(d) { return d.source.id + "-" + d.target.id; });
link.exit().remove();
link = link.enter().append("line").merge(link);
// Update and restart the simulation.
simulation.nodes(data.nodes);
simulation.force("link").links(data.links);
simulation.alpha(1).restart();
}
function ticked() {
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
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; });
}
</script>
https://d3js.org/d3.v4.min.js