Built with blockbuilder.org
forked from sxywu's block: d3unconf 2016, v1
forked from sxywu's block: d3unconf 2016, v2
forked from sxywu's block: d3unconf 2016, v3
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
<script src="https://npmcdn.com/babel-core@5.8.34/browser.min.js"></script>
<script type="text/javascript" src="https://gka.github.io/chroma.js/vendor/chroma-js/chroma.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0;
}
svg {
width: 100%;
height: 100%;
}
text {
font-family: courier;
}
/* blend options taken from visual cinnamon tutorial: https://www.visualcinnamon.com/2016/05/beautiful-color-blending-svg-d3.html */
/*Set isolate on the group element*/
svg {isolation: isolate;}
/*Set blend mode on SVG element: e.g. screen, multiply*/
/* path { mix-blend-mode: multiply; } */
</style>
</head>
<body>
<svg></svg>
<script type="text/babel">
var width = 800;
var height = 500;
var yellow = chroma('#FFFEDE').saturate(3);
var navy = '#000e20';
var blue = chroma(navy).brighten(.5).saturate();
var white = chroma(navy).brighten(4);
var colors = chroma.scale([blue, navy])
.mode('hsl')
.colors(6);
d3.csv('data.csv', (data) => {
var attendees = _.map(data, d => {
return {
name: d['First Name'],
first: d.first.split(', '),
favorite: d.favorite.split(', '),
version: parseInt(d.version.replace('v', '')),
};
});
// create links between the different types
// of firsts, favorites, and versions
var firsts = _.chain(attendees)
.map('first').flatten()
.countBy().toPairs()
.sortBy(d => -d[1])
.filter(d => d[0] !== 'N/A')
.map(0).value();
var favorites = _.chain(attendees)
.map('favorite').flatten()
.countBy().toPairs()
.sortBy(d => -d[1])
.filter(d => d[0])
.map(0).value();
var versions = _.chain(attendees)
.map('version').flatten()
.countBy().toPairs()
// .sortBy(d => -d[1])
.filter(d => d[1] > 1)
.map(0).value();
/**************************************
** calculate nodes and links
**************************************/
// width should be attendee's first experience with dataviz
var startWidth = width * .1;
var perWidth = width / firsts.length * .8;
var xScale = d3.scaleOrdinal().domain(firsts)
.range(_.times(firsts.length, (i) =>
// start at the middle and wrap the points around
((i + firsts.length * .25) % firsts.length) *
perWidth + startWidth));
// height is their current fav d3 API
var startHeight = height * .2;
var perHeight = height / favorites.length * .6;
var yScale = d3.scaleOrdinal().domain(favorites)
.range(_.times(favorites.length, (i) =>
// start at the middle and wrap the points around
((i + favorites.length * .25) % favorites.length) *
perHeight + startHeight));
// YAH LOOPS
var points = [];
_.each(attendees, attendee => {
_.each(attendee.favorite, favorite => {
var x = xScale(favorite);
_.each(attendee.first, first => {
var y = yScale(first);
points.push({
name: attendee.name,
favorite,
first,
version: attendee.version,
focusX: x,
focusY: y,
});
});
});
});
// also loop through the favorites to
// make an actual circle
var outside = [];
var times = 8;
var perWidth = width / times;
var perHeight = height / times;
_.times((times + 1), i => {
outside.push({
fx: 0,
fy: i * perHeight,
});
outside.push({
fx: width,
fy: i * perHeight,
});
});
_.times((times + 1), i => {
outside.push({
fx: i * perWidth,
fy: 0,
});
outside.push({
fx: i * perWidth,
fy: height,
});
});
var nodes = _.union(points, outside);
var simulation = d3.forceSimulation(nodes)
.force('charge', d3.forceManyBody().strength(-25))
.force("collide", d3.forceCollide(2))
.force("x", d3.forceX().x(d => d.focusX))
.force("y", d3.forceY().y(d => d.focusY))
.on("tick", ticked);
var voronoi = d3.voronoi()
.x(d => d.x)
.y(d => d.y);
/**************************************
** draw the circles and links
**************************************/
var svg = d3.select('svg')
.append('g').attr('transform', 'translate(20, 20)');
// motion blur taken from https://www.visualcinnamon.com/2016/05/real-life-motion-effects-d3-visualization.html
var defs = svg.append("defs");
defs.append("filter")
.attr("id", "motionFilter")
.attr('width', '300%')
.attr('height', '300%')
.attr('x', '-100%')
.attr('y', '-100%')
.append("feGaussianBlur")
.attr("in", "SourceGraphic")
.attr("stdDeviation", "2");
var pathContainer = svg.append('g');
var paths, triangles;
var circles = svg.selectAll('g')
.data(points)
.enter().append('g');
// first the blur
circles.append('circle')
.attr('fill', white)
.attr('r', d => Math.max(3 - d.version, 0) * 5)
.attr('opacity', .25)
.style("filter", "url(#motionFilter)");
// then the actual star
circles.selectAll('.star')
.data(d => _.range(Math.max(3 - d.version, 0)))
.enter().append('circle')
.classed('star', true)
.attr('fill', d => !d ? white : 'none')
.attr('stroke', white)
.attr('r', d => d * 5 + 1.5)
.attr('opacity', d => 1 - d * .5)
.attr('stroke-dasharray', '10 2');
// add in text
// var fontSize = 89;
// var text = svg.append('g')
// .attr('opacity', .75);
// text.append('text')
// .attr('text-anchor', 'middle')
// .attr('dy', '.35em')
// .attr('y', -fontSize * 1.15)
// .attr('fill', white)
// .attr('font-size', fontSize * .85)
// .text('d3.unconf');
// text.append('text')
// .attr('text-anchor', 'middle')
// .attr('dy', '.35em')
// .attr('y', fontSize * .3)
// .attr('fill', white)
// .attr('font-size', fontSize * 2.5)
// .text('2016');
// text.append('line')
// .attr('x1', -fontSize * 2)
// .attr('x2', fontSize * 2)
// .attr('y1', fontSize * 1.75)
// .attr('y2', fontSize * 1.75)
// .attr('stroke', white)
// .attr('stroke-width', fontSize / 9)
// .attr('stroke-linecap', 'round')
function ticked() {
circles.attr('transform', (d) =>
'translate(' + [d.x, d.y] + ')');
triangles = voronoi.triangles(nodes);
// now create the triangles
paths = pathContainer.selectAll('path')
.data(triangles);
paths.exit().remove();
paths.enter().append('path')
.merge(paths)
.attr('d', d => {
return 'M' + _.map(d, function(point) {
return point.x + ',' + point.y;
}).join(' L') + 'Z';
}).attr('fill', (d, i) => colors[i % 6])
.attr('stroke', (d, i) => colors[i % 6])
.attr('opacity', .85);
}
});
</script>
</body>
Modified http://gka.github.io/chroma.js/vendor/chroma-js/chroma.min.js to a secure url
https://d3js.org/d3.v4.min.js
https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js
https://npmcdn.com/babel-core@5.8.34/browser.min.js
https://gka.github.io/chroma.js/vendor/chroma-js/chroma.min.js