Inspiration from Mike Bostock's chord diagram examples and others. Also the API reference layout for the chord layout.
forked from SkiWether's block: Chord diagram showing co-occurrences.
license: mit |
Inspiration from Mike Bostock's chord diagram examples and others. Also the API reference layout for the chord layout.
forked from SkiWether's block: Chord diagram showing co-occurrences.
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<!-- affinity group data, circle rotation, auto-resize svg image, chord function, configuration --> | |
<title>Open Chord Shared Ad Blocks</title> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<style> | |
#visual { | |
font: 14px sans-serif; | |
} | |
.chord path { | |
fill-opacity: .67; | |
stroke: #000; | |
stroke-width: .5px; | |
} | |
@media only screen and (min-device-width: 320px) and (max-device-width: 568px) { | |
#visual { | |
-webkit-user-select: none; | |
font-size: 1.2em; | |
} | |
} | |
@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) { | |
#visual { | |
-webkit-user-select: none; | |
} | |
} | |
</style> | |
<body> | |
<script> | |
var visual = document.getElementById("visual"); | |
// shared ad blocks between 7-Up, A&W, Coke, Dr Pepper, Pepsi brands | |
var matrix = [ | |
[ 5, 0, 1, 1, 3 ], | |
[ 0, 0, 0, 1, 0 ], | |
[ 1, 0, 1, 1, 0 ], | |
[ 1, 1, 1, 2, 0 ], | |
[ 3, 0, 0, 0, 3 ] | |
]; | |
var array = [ "Makanan", "Gadget", "Rumah tangga", "Alat kantor", "Handphone" ]; | |
var rotation = -0.7; | |
var chord_options = { | |
"gnames": array, | |
"rotation": rotation, | |
"colors": ["#034e7b","#feb24c","#b10026","#238443","#fdbb84","#ffffb2","#fed976"] | |
}; | |
function Chord(container, options, matrix) { | |
// initialize the chord configuration variables | |
var config = { | |
width: 640, | |
height: 560, | |
rotation: 0, | |
textgap: 26, | |
colors: ["#7fc97f", "#beaed4", "#fdc086", "#ffff99", "#386cb0", "#f0027f", "#bf5b17", "#666666"] | |
}; | |
// add options to the chord configuration object | |
if (options) { | |
extend(config, options); | |
} | |
// set chord visualization variables from the configuration object | |
var offset = Math.PI * config.rotation, | |
width = config.width, | |
height = config.height, | |
textgap = config.textgap, | |
colors = config.colors; | |
// set viewBox and aspect ratio to enable a resize of the visual dimensions | |
var viewBoxDimensions = "0 0 " + width + " " + height, | |
aspect = width / height; | |
if (config.gnames) { | |
gnames = config.gnames; | |
} else { | |
// make a list of names | |
gnames = []; | |
for (var i=97; i<matrix.length; i++) { | |
gnames.push(String.fromCharCode(i)); | |
} | |
} | |
// start the d3 magic | |
var chord = d3.layout.chord() | |
.padding(.05) | |
.sortSubgroups(d3.descending) | |
.matrix(matrix); | |
var innerRadius = Math.min(width, height) * .31, | |
outerRadius = innerRadius * 1.1; | |
var fill = d3.scale.ordinal() | |
.domain(d3.range(matrix.length-1)) | |
.range(colors); | |
var svg = d3.select("body").append("svg") | |
.attr("id", "visual") | |
.attr("viewBox", viewBoxDimensions) | |
.attr("preserveAspectRatio", "xMinYMid") // add viewBox and preserveAspectRatio | |
.attr("width", width) | |
.attr("height", height) | |
.append("g") | |
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); | |
var g = svg.selectAll("g.group") | |
.data(chord.groups) | |
.enter().append("svg:g") | |
.attr("class", "group"); | |
g.append("svg:path") | |
.style("fill", function(d) { return fill(d.index); }) | |
.style("stroke", function(d) { return fill(d.index); }) | |
.attr("id", function(d, i) { return "group" + d.index; }) | |
.attr("d", d3.svg.arc().innerRadius(innerRadius).outerRadius(outerRadius).startAngle(startAngle).endAngle(endAngle)) | |
.on("mouseover", fade(.1)) | |
.on("mouseout", fade(1)); | |
g.append("svg:text") | |
.each(function(d) {d.angle = ((d.startAngle + d.endAngle) / 2) + offset; }) | |
.attr("dy", ".35em") | |
.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 + textgap) + ")" | |
+ (d.angle > Math.PI ? "rotate(180)" : ""); | |
}) | |
.text(function(d) { return gnames[d.index]; }); | |
svg.append("g") | |
.attr("class", "chord") | |
.selectAll("path") | |
.data(chord.chords) | |
.enter().append("path") | |
.attr("d", d3.svg.chord().radius(innerRadius).startAngle(startAngle).endAngle(endAngle)) | |
.style("fill", function(d) { return fill(d.source.index); }) | |
.style("opacity", 1) | |
.append("svg:title") | |
.text(function(d) { | |
return d.source.value + " " + gnames[d.source.index] + " shared with " + gnames[d.target.index]; | |
}); | |
// helper functions start here | |
function startAngle(d) { | |
return d.startAngle + offset; | |
} | |
function endAngle(d) { | |
return d.endAngle + offset; | |
} | |
function extend(a, b) { | |
for( var i in b ) { | |
a[ i ] = b[ i ]; | |
} | |
} | |
// Returns an event handler for fading a given chord group. | |
function fade(opacity) { | |
return function(g, i) { | |
svg.selectAll(".chord path") | |
.filter(function(d) { return d.source.index != i && d.target.index != i; }) | |
.transition() | |
.style("opacity", opacity); | |
}; | |
} | |
window.onresize = function() { | |
var targetWidth = (window.innerWidth < width)? window.innerWidth : width; | |
var svg = d3.select("#visual") | |
.attr("width", targetWidth) | |
.attr("height", targetWidth / aspect); | |
} | |
} | |
window.onload = function() { | |
Chord(visual, chord_options, matrix); | |
} | |
d3.select(self.frameElement).style("height", "600px"); | |
</script> | |
<h1>Shared Ad Blocks by Brand</h1> | |
</body> | |
</html> |