animatedInteractiveNodesAndArcs = {
const radius = 8;
const container = d3.select(DOM.svg(width+margin.left+margin.right,
height+margin.top+margin.bottom))
const arcGroup = container
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
const nodes = arcGroup.selectAll("nodes")
.data(graphData.nodes)
.enter().append("circle")
.attr("cx", d => xScale(d.name))
.attr("cy", height-50)
.attr("r", radius)
.attr("fill", "steelblue")
.attr("id", d => d.id)
arcGroup.selectAll("nodeLabels")
.data(graphData.nodes)
.enter().append("text")
.attr("x", d => xScale(d.name))
.attr("y", height-20)
.attr("fill", "darkgrey")
.style("text-anchor", "middle")
.text(d => d.name)
function buildArc(d) {
let start = xScale(idToNode[d.source].name);
let end = xScale(idToNode[d.target].name);
const arcPath = ['M', start, height-50, 'A', (start - end)/2, ',', (start-end)/2, 0,0,",",
start < end ? 1: 0, end, height-50].join(' ');
return arcPath;
}
// create the arcs
let arcs = arcGroup.selectAll("arcs")
.data(graphData.links)
.enter().append("path")
.style("fill", "none")
.attr("stroke", "none")
.attr("d", d => buildArc(d));
// When the user mouses over a node,
// add interactive highlighting to see connections between nodes
nodes.on('mouseover', function(d) {
// remember which node is the selected node
let thisNode = this;
// highlight the selected node
d3.select(this).style("fill", "firebrick");
// style the arcs
arcs
.style('stroke', function (arcd) {
return arcd.source === d.id ? 'firebrick' : 'none';})
.style('stroke-width', function (arcd) {
return arcd.source === d.id ? 4 : 1;})
.attr("stroke-dasharray", function(arcd) {
return arcd.source === d.id ? this.getTotalLength() : 0;})
.attr("stroke-dashoffset", function(arcd)
{ return arcd.source === d.id ? this.getTotalLength() : 0;})
// reveal the arcs
.transition()
.duration(3000)
.attr("stroke-dashoffset", 0)
/* To make this code clean, I added the idToTargetNodes function in the Data section;
it gets list of nodes linked to from the user-selected node.
This function uses a filter on the nodes selection to find those nodes that are on the target
list or to find the source node itself. The style code that follows is applied only to those
nodes that make it through the filter.
The + before the node IDs makes them both integers instead of strings so that === works.
*/
.on("end", function(d, i) {
// the if statement is to make this function run only once at the end. It isn't required.
if (i === 0)
nodes
.filter(function(noded) {
return idToTargetNodes[thisNode.id].includes(noded.id) || +thisNode.id === +noded.id})
.style("fill", "firebrick");
});
});
// remove highlighting when user mouse moves out of node by restoring default colors and thickness
nodes.on('mouseout', function () {
nodes.style("fill", "steelblue");
arcs.style('stroke', 'none');
arcs.style('stroke-width', 1);
});
return container.node();
}