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 mbostock's block: Modifying a Force Layout
forked from proto1994's block: Modifying a Force Layout
xxxxxxxxxx
<style>
.animate {
animation: ani 1s linear 0s infinite alternate;
}
@keyframes ani {
from {
transform: scale(2);
}
to {
transform: scale(1.5);
}
}
</style>
<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",fid:0, fx: 200, fy:height/2, s: null},
b = {id: "b", fid: 'a', x: 50, y: 80, s: 0, e: 4000},
c = {id: "c", fid: 'b', x: 80, y: 100, s: 0, e: 1656},
d = {id: "d", fid: 'b', x: -50, y: -50, s: 0, e: 1000},
e = {id: "e", fid: 'a', s: 0,e: 3000},
f = {id: "f", fid: 'b', s: 0,e: 3000},
g1 = {id: "g", fid: 'e', s: 0,e: 3000},
h = {id: "h", fid: 'a', s: 0,e: 3000},
i = {id: "i", fid: 'b', s: 0,e: 3000},
nodes = [a],
links = [];
var simulation = d3.forceSimulation(nodes)
.force("charge", d3.forceManyBody().strength(-100))
.force("link", d3.forceLink(links).distance(50))
.on('tick', ticked)
// .force("x", d3.forceX())
// .force("y", d3.forceY())
var g = svg.append("g"),
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");
svg.call(d3.zoom().scaleExtent([0.05, 8]).on('zoom', () => {
// 保存当前缩放的属性值
var transform = d3.event.transform;
g.attr('transform', transform)
})).on('dblclick.zoom', null)
restart([b]);
d3.timeout(function(){
nodes.push(b);
links.push({source: a, target: b}); // Add a-b.
restart([c]);
}, 2000);
// d3.timeout(function(){
// nodes.push(c);
// links.push({source: b, target: c});
// restart([d])
// }, 4000)
// d3.timeout(function(){
// nodes.push(d);
// links.push({source: b, target: d});
// restart([e, f])
// }, 6000)
// d3.timeout(function(){
// nodes.push(e, f);
// links.push({source: a, target: e},
// {source: b, target: f});
// restart([g1, h, i])
// }, 8000)
d3.interval(function() {
node.attr("opacity", d => {
d.s += 1000;
if (d.e < d.s) return .1;
return 1;
})
link.attr("stroke-opacity", d => {
if (d.target.e < d.target.s) return .1;
return 1;
})
}, 1000, d3.now());
// d3.interval(function() {
// nodes.push(c); // Re-add c.
// links.push({source: b, target: c}); // Re-add b-c.
// links.push({source: c, target: a}); // Re-add c-a.
// restart();
// }, 2000, d3.now() + 1000);
function restart(arr) {
// Apply the general update pattern to the nodes.
node = node
.data(nodes, (d) => d.id)
.enter()
.append('g').attr('class', 'node')
.merge(node)
var dom = document.querySelectorAll('.node');
for (let len = dom.length, i = 0; i < len; i++) {
console.log(d3.select(dom[i]). select('circle').empty())
if (d3.select(dom[i]). select('circle').empty()) {
node.append("circle")
.attr("fill", (d) => color(d.id))
.attr('class', (d) => {
if (!arr) return '';
if (arr.filter(res => res.fid == d.id).length > 0) {
return 'animate';
}
return '';
})
.attr("r", 8)
}
}
// Apply the general update pattern to the links.
link = link
.data(links, (d) => { return d.source.id + "-" + d.target.id; });
link = link.enter().append("line").merge(link);
// Update and restart the simulation.
simulation.nodes(nodes);
simulation.force("link").links(links);
simulation.alpha(1).restart();
}
function ticked() {
node.attr("transform", d => `translate(${d.x}, ${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