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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more