xxxxxxxxxx
<svg width="960" height="700"></svg>
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<style>
.link {
fill: none;
}
</style>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
color = ['blue', 'red'];
var defs = svg.append('svg:defs');
defs.append('svg:marker')
.attr('id', 'end-arrow')
.attr('viewBox', '0 -5 10 10')
.attr('refX', 16)
.attr('refY', -1)
.attr('markerWidth', 6)
.attr('markerHeight', 6)
.attr('orient', 'auto')
.append('svg:path')
.attr('d', 'M0,-5L10,0L0,5')
.attr('fill', '#666');
var nodes = [],
links = [],
i = 1;
const getLink = (link) => {
const {source: s, target: t} = link;
var source = nodes.find(n => n.id === s);
var target = nodes.find(n => n.id === t);
return { source, target };
}
const updateNodes = (newD) => {
nodes.forEach((d, i) => {
var newNode = newD.find(n => n.id === d.id);
if (newNode) {
nodes[i].weight = newNode.weight;
}else {
nodes[i] = null;
}
})
nodes = nodes.filter(n => !!n);
newNodes = newD.filter(d => !nodes.find(n => n.id === d.id));
nodes.push(...newNodes);
};
const matchLinks = (n, d) => n.source.id === d.source.id && n.target.id === d.target.id;
d3.json('000.json', (err, data) => {
nodes = _.cloneDeep(data.nodes);
updateNodes(nodes);
var simulation = d3.forceSimulation(nodes)
.force("charge", d3.forceManyBody().strength(-100))
.force("link", d3.forceLink(links).distance(50))
.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("path"),
node = g.append("g").attr("stroke", "#fff").attr("stroke-width", 1.5).selectAll(".node");
restart();
const pad = num => `00${i}`.slice(-3);
d3.timeout(function() {
links = (data.links.map(getLink))
restart();
}, 1000);
d3.interval(() => {
d3.json(`${pad(i)}.json`, (err, data) => {
i++;
console.log(data.nodes[2]);
newNodes = _.cloneDeep(data.nodes);
updateNodes(newNodes);
console.log(nodes[2])
newLinks = _.cloneDeep(data.links);
links = data.links.map(getLink);
restart()
})
}, 1000, 2000);
function restart() {
// Apply the general update pattern to the nodes.
node = node.data(nodes, (d) => d.id);
node
.attr("fill", d => color[d.weight])
node.exit().transition()
.duration(300)
.attr("opacity", 0)
.remove()
node = node.enter()
.append("circle")
.attr("fill", (d) => color[d.value])
.attr("r", 6)
.merge(node);
node.append('title').text(d => d.id)
// Apply the general update pattern to the links.
link = link.data(links, (d, i) => {
return `${d.source.id}-${d.target.id}`;
});
link.exit()
.attr("marker-end", "none")
.transition()
.duration(200)
.attr("stroke", "red")
.attr("stroke-opacity", 0)
.attrTween("x1", (d) => () => d.source.x)
.attrTween("x2", (d) => () => d.target.x)
.attrTween("y1", (d) => () => d.source.y)
.attrTween("y2", (d) => () => d.target.y)
.remove();
link = link.enter().append("svg:path")
.attr("marker-end", "url(#end-arrow)")
.call((link) => link
.attr("stroke", "green")
.attr("stroke-opacity", 0)
.transition()
.duration(300)
.attr("stroke-opacity", 0.6)
.attr("stroke", "#333333")
.attr("fill", "none")
.attr("marker-end", "url(#end-arrow)"))
.merge(link);
// Update and restart the simulation.
simulation.nodes(nodes);
simulation.force("link").links(links);
simulation.alpha(1).restart();
}
function ticked() {
node
.transition()
.duration(2)
.attr("cx", (d) => d.x)
.attr("cy", (d) => d.y)
link.attr("d", function(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy)*1.5;
return "M" +
d.source.x + "," +
d.source.y + "A" +
dr + "," + dr + " 0 0,1 " +
d.target.x + "," +
d.target.y;
});
}
})
</script>
https://d3js.org/d3.v4.js
https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js