Published
Edited
Apr 19, 2021
Insert cell
Insert cell
Insert cell
Insert cell
graphData = d3.json("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/data_network.json")
Insert cell
Insert cell
allNodesNames = graphData.nodes.map(function(d){return d.name});
Insert cell
Insert cell
idToNode =
{
let dict = {};
graphData.nodes.forEach(function(n) {
dict[n.id] = n;
});
return dict;
}
Insert cell
Insert cell
idToTargetNodes =
{
let dict = {};
graphData.nodes.forEach(function (n) {
dict[n.id] = [];
graphData.links.forEach(function (l) {
if (l.source === n.id) {
dict[n.id].push(l.target);
}
});
});
return dict;
}
Insert cell
Insert cell
total_height = 300
Insert cell
total_width = 450
Insert cell
margin = ({top: 20, bottom: 20, left: 30, right: 30});
Insert cell
height = total_height - margin.top - margin.bottom;
Insert cell
width = total_width - margin.left - margin.right;
Insert cell
Insert cell
xScale = d3.scalePoint()
.domain(allNodesNames)
.range([0,width])
Insert cell
Insert cell
// Adapted from https://www.d3-graph-gallery.com/graph/arc_basic.html
justNodesAndLabels = {
const radius = 8
// create a container for the svg; use standard margin notation
const container = d3.select(DOM.svg(total_width,
total_height))
// create a group to hold the viz, adjust the coordinates using standard margin notation
const arcGroup = container
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
// create the nodes and labels similarly to Lab 1;
// the new part here is that x locations are determined by the xScale function;
// we also add some padding to y to move it away from the margins
// we assign an attribute named id, as this can be useful to have later
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)
return container.node();
}
Insert cell
Insert cell
yScale = d3.scalePoint()
.domain(allNodesNames)
.range([0,height])
Insert cell
// Adapted for node and y-scale, using the x-scale example

// Adapted from https://www.d3-graph-gallery.com/graph/arc_basic.html
justNodesAndLabels_y = {
const radius = 8
// create a container for the svg; use standard margin notation
const container = d3.select(DOM.svg(width+margin.left+margin.right,
height+margin.top+margin.bottom))
// create a group to hold the viz, adjust the coordinates using standard margin notation
const arcGroup = container
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
// create the nodes and labels similarly to Lab 1;
// the new part here is that x locations are determined by the xScale function;
// we also add some padding to y to move it away from the margins
// we assign an attribute named id, as this can be useful to have later
arcGroup.selectAll("nodes")
.data(graphData.nodes)
.enter().append("circle")
.attr("cx", margin.left + 30)
.attr("cy", d => yScale(d.name))
.attr("r", radius)
.attr("fill", "steelblue")
arcGroup.selectAll("nodeLabels")
.data(graphData.nodes)
.enter().append("text")
.attr("x", margin.left)
.attr("y", d => yScale(d.name))
.attr("fill", "darkgrey")
.style("alignment-baseline", "central")
.text(d => d.name)
return container.node();
}
Insert cell
Insert cell
// Adapted from https://www.d3-graph-gallery.com/graph/arc_basic.html
nodesAndArcs = {
const radius = 8
// select the DIV defined in the HTML above; this is where the svg will go; define its size
const container = d3.select(DOM.svg(total_width,
total_height))
// create a group to hold the viz, adjust the coordinates
const arcGroup = container
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
// Place the nodes use graphData.nodes for the data;
// the x locations are determined by the xScale function as above
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) {
// d.source and d.target are the locations in graphData.links
// xScale takes a node name and finds its location on the x axis from 0 to width
// So start is the location in pixels of the start of the arc
let start = xScale(idToNode[d.source].name);
let end = xScale(idToNode[d.target].name);
// This code builds up the SVG arc path element
const arcPath = ['M', // start the path
start, height-50, // declare the (x,y) of where to start
'A', // specify an eliptical curve
(start - end)/2, ',', // xradius: height of arc is proportional to start - end
(start - end)/2, // yradius
0, 0, ",", // rotation of ellipse is 0 along x and y; see arc url for details
start < end ? 1: 0, // make all arcs curve above the nodes; see arc documentation
end, height-50] // declare (x,y) of endpoint
.join(' '); // convert the bracketed array into a string
return arcPath;
};
// to create the arcs, we use graphData.links instead of graphData.nodes
arcGroup.selectAll("arcs")
.data(graphData.links)
.enter().append("path")
.attr("d", d => buildArc(d))
.style("fill", "none") // no fill color for the arcs
.attr("stroke", "black") // make the arc's lines be black
return container.node();
}
Insert cell
graphData
Insert cell
Insert cell
// Adapted from https://www.d3-graph-gallery.com/graph/arc_basic.html
nodesAndArcs_long = {
const radius = 8
// select the DIV defined in the HTML above; this is where the svg will go; define its size
const container = d3.select(DOM.svg(width+margin.left+margin.right,
height+margin.top+margin.bottom))
// create a group to hold the viz, adjust the coordinates
const arcGroup = container
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
// Place the nodes use graphData.nodes for the data;
// the x locations are determined by the xScale function as above
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) {
// d.source and d.target are the locations in graphData.links
// xScale takes a node name and finds its location on the x axis from 0 to width
// So start is the location in pixels of the start of the arc
let start = xScale(idToNode[d.source].name);
let end = xScale(idToNode[d.target].name);
// This code builds up the SVG arc path element
const arcPath = ['M', // start the path
start, height-50, // declare the (x,y) of where to start
'A', // specify an eliptical curve
(start - end)/2, ',', // xradius: height of arc is proportional to start - end
(start - end)/2, // yradius
0, 0, ",", // rotation of ellipse is 0 along x and y; see arc url for details
start < end ? 1: 0, // make all arcs curve above the nodes; see arc documentation
end, height-50] // declare (x,y) of endpoint
.join(' '); // convert the bracketed array into a string
if (d.target-d.source !=1) { // ADD ONE LINE HERE: arcs that link adjacent nodes are not shown (in other words, do not show arcs that are length 1)
return arcPath;}
};
// to create the arcs, we use graphData.links instead of graphData.nodes
arcGroup.selectAll("arcs")
.data(graphData.links)
.enter().append("path")
.attr("d", d => buildArc(d))
.style("fill", "none") // no fill color for the arcs
.attr("stroke", "black") // make the arc's lines be black
return container.node();
}
Insert cell
Insert cell
animatedNodes1 = {
const radius = 8
const container = d3.select(DOM.svg(total_width,
total_height))
const arcGroup = container
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
// First place the nodes at cy position 50
// Then call transition(), with a duration of i*500, moving to cy position height-50
// For fun, we've put in a bouncing ease transition type. Try changing it around!
const nodes = arcGroup.selectAll("nodes")
.data(graphData.nodes)
.enter().append("circle")
.attr("cx", d => xScale(d.name))
.attr("cy", 50)
.attr("r", radius)
.attr("fill", "steelblue")
.attr("id", d => d.id)
.transition()
.ease(d3.easeBounce)
.duration((d,i) => i*200)
.attr("cy", height-50);
return container.node();
}
Insert cell
Insert cell
animatedNodes_800 = {
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 + ")")
// First place the nodes at cy position 50
// Then call transition(), with a duration of i*500, moving to cy position height-50
// For fun, we've put in a bouncing ease transition type. Try changing it around!
const nodes = arcGroup.selectAll("nodes")
.data(graphData.nodes)
.enter().append("circle")
.attr("cx", d => xScale(d.name))
.attr("cy", 50)
.attr("r", radius)
.attr("fill", "steelblue")
.attr("id", d => d.id)
.transition()
.ease(d3.easeBounce)
.duration((d,i) => i*800) // Change to larger than 500 (a) change the value from 500 to a larger or smaller number and see what happens
.attr("cy", height-50);
return container.node();
}
Insert cell
animatedNodes_500 = {
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 + ")")
// First place the nodes at cy position 50
// Then call transition(), with a duration of i*500, moving to cy position height-50
// For fun, we've put in a bouncing ease transition type. Try changing it around!
const nodes = arcGroup.selectAll("nodes")
.data(graphData.nodes)
.enter().append("circle")
.attr("cx", d => xScale(d.name))
.attr("cy", 50)
.attr("r", radius)
.attr("fill", "steelblue")
.attr("id", d => d.id)
.transition()
.ease(d3.easeBounce)
.duration((d,i) => 500) // (b) change it back to 500 and remove the multiplication by the index i and see what happens
.attr("cy", height-50);
return container.node();
}
Insert cell
Insert cell
animatedNodes2 = {
const radius = 8
const container = d3.select(DOM.svg(total_width,
total_height))
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", 50)
.attr("r", radius)
.attr("fill", "steelblue")
.attr("id", d => d.id)
.transition()
.ease(d3.easeBounce)
.duration((d,i) => i*500)
.attr("cy", height-50);
/* This invokes additional transitions on the nodes selection;
Try commenting it out and putting it back in.
Add more transitions between the fill and the cy. */
nodes
.transition()
.style("fill", "firebrick")
.transition()
.attr("cy", 50)
return container.node();
}
Insert cell
Insert cell
animatedNodes2_circle = {
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", 50)
.attr("r", radius)
.attr("fill", "steelblue")
.attr("id", d => d.id)
.transition()
.ease(d3.easeBounce)
.duration((d,i) => i*500)
.attr("cy", height-50);
/* This invokes additional transitions on the nodes selection;
Try commenting it out and putting it back in.
Add more transitions to the or in the middle. */
nodes
.transition()
.attr("fill", "firebrick")
.attr("r",radius*10) //add one line to the code above to make the circles grow to a larger size as they turn from red to blue
.transition()
.attr("cy", 50)
return container.node();
}
Insert cell
Insert cell
animatedNodes3 = {
const radius = 8
const container = d3.select(DOM.svg(total_width,
total_height))
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", 50)
.attr("r", radius)
.attr("fill", "steelblue")
.attr("id", d => d.id)
.transition()
.ease(d3.easeBounce)
.duration((d,i) => i*500)
.attr("cy", height-50)
.attr("fill", "firebrick")
.on("end", function() {
d3.select(this)
.transition()
.attr("cy", 50)
})
return container.node();
}
Insert cell
Insert cell
animatedNodes_circleend = {
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", 50)
.attr("r", radius)
.attr("fill", "steelblue")
.attr("id", d => d.id)
.transition()
.ease(d3.easeBounce)
.duration((d,i) => i* 500)
.attr("cy", height-50)

nodes.transition()
.ease(d3.easeLinear)
.attr("fill", "firebrick")
.on("end", function(d, i) {
d3.select(this)
.transition()
.duration(500)
.attr("cy", 50)
.attr("r", 40) //add code to make the circles get larger after they change to their final positions
})
return container.node();
}
Insert cell
Insert cell
expandingAnimatedNodes = {
const radius = 8
const container = d3.select(DOM.svg(total_width,
total_height))
// give the svg a black background
container.style("background-color", "white")
const arcGroup = container
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
// define a bright red color; used the color meter on the pitch interactive viz
const redcol = d3.rgb("252", "0", "8");
// first just place the red nodes at opacity 1
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", redcol)
.attr("opacity", 1)
container.transition().duration(50).style("background-color", "black")

// now do the animations. Grow the size and then fade out the circles.
// at the end, fade the black background white.
nodes
.transition(d3.easeBounce)
.duration(1000)
.attr("r", 20).attr('opacity', 0.3)
.transition()
.ease(d3.easeLinear)
.duration(2000)
.attr("opacity", 0)
.on("end", function() {
container.transition().duration(1000).style("background-color", "white");
})
return container.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
// adapted from https://www.d3-graph-gallery.com/graph/arc_highlight.html
// and https://observablehq.com/@lemonnish/svg-path-animations-d3-transition
animateArcsFromNodes = {
const radius = 8;
const container = d3.select(DOM.svg(total_width,
total_height))
const arcGroup = container
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
// create the nodes
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)
// create the node labels
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)
// This code builds up the SVG path element; see nodesAndArcs for details
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
const arcs = arcGroup.selectAll("arcs")
.data(graphData.links)
.enter().append("path")
.style("fill", "none")
.attr("stroke", "black")
.attr("d", d => buildArc(d))
// do the animation; see the posts on arc animation for explanation
arcs
// hide the arcs
.attr("stroke-dasharray", function () {
return this.getTotalLength()
})
.attr("stroke-dashoffset", function () {
return this.getTotalLength()
})
// reveal the arcs
.transition()
.duration(4000)
.attr("stroke-dashoffset", 0)
// hide them again
.transition()
.attr("stroke-dasharray", function () {
return this.getTotalLength()
})
.attr("stroke-dashoffset", function () {
return this.getTotalLength()
})
return container.node();
}
Insert cell
Insert cell
// adapted from https://www.d3-graph-gallery.com/graph/arc_highlight.html
animateArcsFromNodesThenExplode = {
const radius = 8;
const container = d3.select(DOM.svg(total_width,
total_height))
const arcGroup = container
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
// create the nodes
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)
// create the node labels
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)
// This code builds up the SVG path element; see nodesAndArcs for details
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
const arcs = arcGroup.selectAll("arcs")
.data(graphData.links)
.enter().append("path")
.style("fill", "none")
.attr("stroke", "black")
.attr("d", d => buildArc(d))
// do the animation; see the posts on arc animation for explanation
arcs
// hide the arcs
.attr("stroke-dasharray", function () {
return this.getTotalLength()
})
.attr("stroke-dashoffset", function () {
return this.getTotalLength()
})
// reveal the arcs
.transition()
.duration(4000)
.attr("stroke-dashoffset", 0)
// make circles exlode
.on("end", function() {
const redcol = d3.rgb("252", "0", "8");
nodes
.attr("fill", redcol)
.attr("opacity", 1)
.transition(d3.easeBounce)
.duration(1000)
.attr("r", 20).attr('opacity', 0.3)
.transition()
.ease(d3.easeLinear)
.duration(1000)
.attr("opacity", 0)
//hide the arcs again
.on("end", function() {
arcs
.transition()
.ease(d3.easeLinear)
.duration(1000)
.attr("stroke-dasharray", function () {
return this.getTotalLength()
})
.attr("stroke-dashoffset", function () {
return this.getTotalLength()
})
});
})
return container.node();
}
Insert cell
Insert cell
// adapted from https://www.d3-graph-gallery.com/graph/arc_highlight.html
interactiveNodesAndArcs = {
const radius = 8
const container = d3.select(DOM.svg(total_width,
total_height))
const arcGroup = container
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
// create the nodes
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)
// create the node labels
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)
// This code builds up the SVG path element; see nodesAndArcs for details
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
const arcs = arcGroup.selectAll("arcs")
.data(graphData.links)
.enter().append("path")
.attr("d", d => buildArc(d))
.style("fill", "none")
.attr("stroke", "black")
// When the user mouses over a node,
// add interactive highlighting to see connections between nodes
nodes.on('mouseover', function(d) {
// highlight only the selected node
d3.select(this).style("fill", "firebrick");
// next, style the arcs
arcs
// the arc color and thickness stays as the default unless connected to the selected node d
// notice how embedding the reference to arcs within nodes.on() allows the code to connect d to arcd
// this code iterates through all the arcs so we can compare each to the selected node d
.style('stroke', function (arcd) {
return arcd.source === d.id || arcd.target === d.id ? 'firebrick' : 'black';})
.style('stroke-width', function (arcd) {
return arcd.source === d.id || arcd.target === d.id ? 4 : 1;})
});
// remove highlighting when user mouse moves out of node by restoring default colors and thickness
nodes.on('mouseout', function (d) {
nodes.style("fill", "steelblue");
arcs.style('stroke', 'black');
arcs.style('stroke-width', 1);
});
return container.node();
}
Insert cell
Insert cell
// adapted from https://www.d3-graph-gallery.com/graph/arc_highlight.html
interactiveNodesAndArcs_colorchange = {
const radius = 8
const container = d3.select(DOM.svg(total_width,
total_height))
const arcGroup = container
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
// create the nodes
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)
// create the node labels
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)
// This code builds up the SVG path element; see nodesAndArcs for details
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
const arcs = arcGroup.selectAll("arcs")
.data(graphData.links)
.enter().append("path")
.attr("d", d => buildArc(d))
.style("fill", "none")
.attr("stroke", "black")
// When the user mouses over a node,
// add interactive highlighting to see connections between nodes
nodes.on('mouseover', function(d) {
// highlight only the selected node, CHANGE HIGHLIGHT COLOR ON HOVER FROM RED TO TURQUOISE
d3.select(this).style("fill", "#45f9c9");
// next, style the arcs
arcs
// the arc color and thickness stays as the default unless connected to the selected node d
// notice how embedding the reference to arcs within nodes.on() allows the code to connect d to arcd
// this code iterates through all the arcs so we can compare each to the selected node d
.style('stroke', function (arcd) {
return arcd.source === d.id || arcd.target === d.id ? '#45f9c9' : '#45f9c9';}) // CHANGE COLOR TO ALL TURQUOISE
.style('stroke-width', function (arcd) {
return arcd.source === d.id || arcd.target === d.id ? 4 : 1;})
});
// remove highlighting when user mouse moves out of node by restoring default colors and thickness
nodes.on('mouseout', function (d) {
nodes.style("fill", "steelblue");
arcs.style('stroke', 'black');
arcs.style('stroke-width', 1);
});
return container.node();
}
Insert cell
Insert cell
Insert cell
// code goes here
// adapted from https://www.d3-graph-gallery.com/graph/arc_highlight.html
AnimateArcsFromNodes_Explode = {
const radius = 8;
const container = d3.select(DOM.svg(total_width,
total_height))
const arcGroup = container
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
// create the nodes
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)
// create the node labels
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)
// This code builds up the SVG path element; see nodesAndArcs for details
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
const arcs = arcGroup.selectAll("arcs")
.data(graphData.links)
.enter().append("path")
.style("fill", "none")
.attr("stroke", "black")
.attr("d", d => buildArc(d))
// do the animation; see the posts on arc animation for explanation
arcs
// hide the arcs
.attr("stroke-dasharray", function () {
return this.getTotalLength()
})
.attr("stroke-dashoffset", function () {
return this.getTotalLength()
})
// reveal the arcs
.transition()
.duration(4000)
.attr("stroke-dashoffset", 0)
// make circles exlode
.on("end", function() {
const redcol = d3.rgb("255", "69", "0");
nodes
.attr("fill", redcol)
.attr("opacity", 1)
.transition(d3.easeBounce)
.duration(800)
.attr("r", 20).attr('opacity', 0.2)
.transition()
.ease(d3.easeLinear)
.duration(800)
.attr("opacity", 0)
//hide the arcs again
.on("end", function() {
arcs
.transition()
.ease(d3.easeLinear)
.duration(1000)
.attr("stroke-dasharray", function () {
return this.getTotalLength()
})
.attr("stroke-dashoffset", function () {
return this.getTotalLength()
})
.attr("fill", "#ff4500")
});
})
return container.node();
}
Insert cell
Insert cell
d3 = require("d3")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more