Skip to content

Instantly share code, notes, and snippets.

@nbremer nbremer/.block
Last active Jun 18, 2016

Embed
What would you like to do?
Data based orientations in SVG Gradients - Step 2
height: 700

This Chord diagram shows the second step of the example used in my blog on Data-based orientations for gradients in a d3.js Chord Diagram

What you see here are movie collaborations between the Avengers in the MCU (up until & including Thor Ragnarok). Because collaborations are symmetrical, the chords are all as thick at the beginnen as at the end. Therefore, I wanted each chord to represent both the Avengers that it connects through a gradient. This is what you see in this step. In the next step we'll look at how to improve the chord ordering to prevent as much overlap as possible.

You can find the first step and those after this step here

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<title>Step 2 - Collaborations between MCU Avengers</title>
<!-- D3.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
<!-- Google Fonts -->
<link href='https://fonts.googleapis.com/css?family=Lato:400,900' rel='stylesheet' type='text/css'>
<style>
body {
font-size: 12px;
font-family: 'Lato', sans-serif;
text-align: center;
fill: #2B2B2B;
cursor: default;
}
@media (min-width: 600px) {
#chart{
font-size: 16px;
}
}
</style>
</head>
<body>
<div id = "chart"></div>
<script src = "script.js"></script>
</body>
</html>
////////////////////////////////////////////////////////////
//////////////////////// Set-Up ////////////////////////////
////////////////////////////////////////////////////////////
var margin = {left:90, top:90, right:90, bottom:90},
width = Math.min(window.innerWidth, 700) - margin.left - margin.right,
height = Math.min(window.innerWidth, 700) - margin.top - margin.bottom,
innerRadius = Math.min(width, height) * .39,
outerRadius = innerRadius * 1.1;
var Names = ["Black Widow","Captain America","Hawkeye","the Hulk","Iron Man","Thor"],
colors = ["#301E1E", "#083E77", "#342350", "#567235", "#8B161C", "#DF7C00"],
opacityDefault = 0.8;
var matrix = [
[0,4,3,2,5,2], //Black Widow
[4,0,3,2,4,3], //Captain America
[3,3,0,2,3,3], //Hawkeye
[2,2,2,0,3,3], //The Hulk
[5,4,3,3,0,2], //Iron Man
[2,3,3,3,2,0], //Thor
];
////////////////////////////////////////////////////////////
/////////// Create scale and layout functions //////////////
////////////////////////////////////////////////////////////
var colors = d3.scale.ordinal()
.domain(d3.range(Names.length))
.range(colors);
var chord = d3.layout.chord()
.padding(.15)
.sortChords(d3.descending)
.matrix(matrix);
var arc = d3.svg.arc()
.innerRadius(innerRadius*1.01)
.outerRadius(outerRadius);
var path = d3.svg.chord()
.radius(innerRadius);
////////////////////////////////////////////////////////////
////////////////////// Create SVG //////////////////////////
////////////////////////////////////////////////////////////
var svg = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + (width/2 + margin.left) + "," + (height/2 + margin.top) + ")");
////////////////////////////////////////////////////////////
/////////////// Create the gradient fills //////////////////
////////////////////////////////////////////////////////////
//Function to create the unique id for each chord gradient
function getGradID(d){ return "linkGrad-" + d.source.index + "-" + d.target.index; }
//Create the gradients definitions for each chord
var grads = svg.append("defs").selectAll("linearGradient")
.data(chord.chords())
.enter().append("linearGradient")
//Create the unique ID for this specific source-target pairing
.attr("id", getGradID)
.attr("gradientUnits", "userSpaceOnUse")
//Find the location where the source chord starts
.attr("x1", function(d,i) { return innerRadius * Math.cos((d.source.endAngle-d.source.startAngle)/2 + d.source.startAngle - Math.PI/2); })
.attr("y1", function(d,i) { return innerRadius * Math.sin((d.source.endAngle-d.source.startAngle)/2 + d.source.startAngle - Math.PI/2); })
//Find the location where the target chord starts
.attr("x2", function(d,i) { return innerRadius * Math.cos((d.target.endAngle-d.target.startAngle)/2 + d.target.startAngle - Math.PI/2); })
.attr("y2", function(d,i) { return innerRadius * Math.sin((d.target.endAngle-d.target.startAngle)/2 + d.target.startAngle - Math.PI/2); })
//Set the starting color (at 0%)
grads.append("stop")
.attr("offset", "0%")
.attr("stop-color", function(d){ return colors(d.source.index); });
//Set the ending color (at 100%)
grads.append("stop")
.attr("offset", "100%")
.attr("stop-color", function(d){ return colors(d.target.index); });
////////////////////////////////////////////////////////////
////////////////// Draw outer Arcs /////////////////////////
////////////////////////////////////////////////////////////
var outerArcs = svg.selectAll("g.group")
.data(chord.groups)
.enter().append("g")
.attr("class", "group")
.on("mouseover", fade(.1))
.on("mouseout", fade(opacityDefault));
outerArcs.append("path")
.style("fill", function(d) { return colors(d.index); })
.attr("d", arc);
////////////////////////////////////////////////////////////
////////////////////// Append Names ////////////////////////
////////////////////////////////////////////////////////////
//Append the label names on the outside
outerArcs.append("text")
.each(function(d) { d.angle = (d.startAngle + d.endAngle) / 2; })
.attr("dy", ".35em")
.attr("class", "titles")
.attr("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
.attr("transform", function(d) {
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
+ "translate(" + (outerRadius + 10) + ")"
+ (d.angle > Math.PI ? "rotate(180)" : "");
})
.text(function(d,i) { return Names[i]; });
////////////////////////////////////////////////////////////
////////////////// Draw inner chords ///////////////////////
////////////////////////////////////////////////////////////
svg.selectAll("path.chord")
.data(chord.chords)
.enter().append("path")
.attr("class", "chord")
//change the fill to reference the unique gradient ID of the source-target combination
.style("fill", function(d){ return "url(#" + getGradID(d) + ")"; })
.style("opacity", opacityDefault)
.attr("d", path);
////////////////////////////////////////////////////////////
////////////////// Extra Functions /////////////////////////
////////////////////////////////////////////////////////////
//Returns an event handler for fading a given chord group.
function fade(opacity) {
return function(d,i) {
svg.selectAll("path.chord")
.filter(function(d) { return d.source.index != i && d.target.index != i; })
.transition()
.style("opacity", opacity);
};
}//fade
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.