now with singular pronouns!
an iteration on @sxywu‘s block d3.unconf 2016, v10
built with sublime text 3, iterm2, live-server, Chrome, lebab, and gistup
<!DOCTYPE html>
<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 {
/* background-color: #000; */
font-family: courier;
text-align: center;
}
h1 {
font-size: 36px;
width: 1000px;
padding-top: 40px;
}
#header {
width: 1000px;
/* padding: 40px 0; */
position: relative;
margin: auto;
}
#legend {
width: 580px;
height: 600px;
margin: auto;
}
#annotation {
width: 1000px;
position: absolute;
top: 0;
font-size: 20px;
}
#content {
position: relative;
width: 1000px;
padding-bottom: 100px;
margin: auto;
}
#flowers {
width: 800px;
height: 1000px;
margin: auto;
}
#hover {
position: absolute;
width: 250px;
border: 1px solid #666;
border-radius: 5px;
box-shadow: 0 0 5px #666;
text-align: center;
background-color: #fff;
padding: 10px;
}
</style>
</head>
<body>
<div id='header'>
<h1>d3.unconf 2016</h1>
<p>(Hover for attendee info ✨)</p>
<svg id='legend'></svg>
<div id='annotation'></svg>
</div>
<div id='content'>
<svg id='flowers'></svg>
<div id='hover'></div>
</div>
<script lang="babel" type="text/babel">
const midpoint = -2/3;
const halfSide = Math.sqrt(1 / 3);
const triangle = [
`${-halfSide},-1`,
`${halfSide},-1`,
'0,0'
];
const favoritesScale = d3.scaleLinear()
.range([midpoint - .05, -.85]);
const versionsScale = d3.scaleOrdinal()
.range(_.rangeRight(3, 7));
const firstsScale = d3.scaleOrdinal();
const hexScale = 40;
const hexSize = hexScale * 2 / Math.sqrt(3);
d3.csv('data.csv', data => {
/**************************************
** process data and add them to scales as domain
**************************************/
const attendees = _.map(data, d => {
return {
name: d['First Name'],
first: d.first.split(', ')[0],
favorite: d.favorite.split(', ')[0],
version: parseInt(d.version.replace('v', '')),
possesivePronoun: d.possesivePronoun
};
});
const firsts = _.chain(attendees)
.map('first').flatten()
.countBy().toPairs()
.sortBy(d => -d[1])
.map(0).value();
const favorites = _.chain(attendees)
.map('favorite').flatten()
.countBy().value();
const versions = _.chain(attendees)
.map('version').flatten()
.countBy().toPairs()
.sortBy(d => -d[1])
.filter(d => d[1] > 1)
.map(0).value();
// update colors with number of classes
const colors = chroma.cubehelix()
.start(280)
// .rotations(-1 / firsts.length)
.gamma(0.5)
.lightness([0.3, 0.4])
.scale() // convert to chroma.scale
// .correctLightness()
.colors(firsts.length);
firstsScale.domain(firsts).range(colors);
versionsScale.domain(_.range(1, 5));
const maxFavorites = _.max(_.values(favorites));
favoritesScale.domain([1, maxFavorites]);
/**************************************
** legend
**************************************/
const baseLegendData = {
version: 1,
first: 'Bar chart',
favorite: 'd3.scale',
};
const legend = d3.select('#legend');
const annotation = d3.select('#annotation');
// first, the versions
// annotation.append('div')
// .style('position', 'absolute')
// .style('width', '1000px')
// .style('top', hexScale)
// .html('Which version of d3 did you start with?');
const versionsData = _.times(4, i => {
return Object.assign(_.clone(baseLegendData), {
version: i + 1,
legendText: `d3 v${i + 1}`,
});
});
legend.append('g')
.attr('transform', `translate(${[0, 0]})`)
.call(drawHexagons, versionsData, 4);
// first
// annotation.append('div')
// .style('position', 'absolute')
// .style('width', '1000px')
// .style('top', 5.5 * hexScale)
// .html('Your first d3 project:<br />what type of visualization was it?');
const firstData1 = _.times(4, i => {
const index = !i ? i : i * 4 - 1;
return Object.assign(_.clone(baseLegendData), {
first: firsts[index],
legendText: firsts[index],
});
});
const firstData2 = _.times(3, i => {
const index = (!i ? i : i * 4 - 1) + 14;
return Object.assign(_.clone(baseLegendData), {
first: firsts[index],
legendText: firsts[index],
});
});
legend.append('g')
.attr('transform', `translate(${[0, 4 * hexScale]})`)
.call(drawHexagons, firstData1, 4);
legend.append('g')
.attr('transform', `translate(${[1.9 * hexScale, 6.75 * hexScale]})`)
.call(drawHexagons, firstData2, 3);
// favorites
// annotation.append('div')
// .style('position', 'absolute')
// .style('width', '1000px')
// .style('top', 13.5 * hexScale)
// .html('What is your favorite d3 api function?');
const favoriteNames = ['d3.scale', 'd3.transition', 'd3.forceSimulation', 'd3.hierarchy'];
const favoriteData = _.times(4, i => {
return Object.assign(_.clone(baseLegendData), {
favorite: favoriteNames[i],
legendText: favoriteNames[i],
});
});
legend.append('g')
.attr('transform', `translate(${[0, 10.75 * hexScale]})`)
.call(drawHexagons, favoriteData, 4);
/**************************************
** draw the hexagons
**************************************/
const perRow = 5;
const svg = d3.select('#flowers').append('g')
.call(drawHexagons, attendees, perRow);
function drawHexagons(selection, data, perRow) {
const hex = selection.selectAll('.hex')
.data(data).enter().append('g')
.classed('hex', true)
.attr('transform', (d, i) => {
const row = Math.floor(i / perRow);
let x = (i % perRow * 3.2 + 1.5) * hexSize;
if (row % 2) {
x += 1.6 * hexSize;
}
const y = (row * 1.05 + 2) * hexScale;
d.x = x;
d.y = y;
return `translate(${[x, y]})scale(${hexScale})`;
}).on('mouseenter', mouseenter)
.on('mouseleave', mouseleave);
const triangles = hex.selectAll('triangle')
.data((d, j) => {
return _.times(versionsScale(d.version), i => {
return {
angle: i * 60,
midpoint: favoritesScale(favorites[d.favorite]),
color: firstsScale(d.first),
// color: colors[j],
};
});
}).enter().append('g')
.classed('triangle', true)
.attr('transform', d => `rotate(${d.angle})`);
triangles.append('polygon')
.attr('points', triangle.join(' '))
.attr('fill', d => chroma(d.color).darken(2.5));
// .attr('fill', '#fff');
triangles.selectAll('.subtri')
.data(d => _.times(3, i => {
return Object.assign({
subangle: i * 120,
subcolor: chroma(d.color).saturate(1.5).brighten(i),
}, d);
})).enter().append('polygon')
.classed('subtri', true)
.attr('transform', d => `rotate(${d.subangle} 0 ${midpoint})`)
.attr('points', d => {
return [
`${-halfSide},-1`,
`0,${d.midpoint}`,
`${halfSide},-1`
].join(' ');
}).attr('fill', d => d.subcolor);
// add in text if it's for legend
const fontSize = 14;
hex.filter(d => d.legendText)
.append('text')
.attr('y', 1.25)
.attr('font-size', fontSize / hexScale)
.attr('text-anchor', 'middle')
.attr('dy', '.35em')
.text(d => d.legendText);
}
});
/**************************************
** hover interaction
**************************************/
const hover = d3.select('#hover')
.style('display', 'none');
function mouseenter(d) {
if (!d.name) return;
let html = `<h2>${d.name}</h2>`;
html += `<p>started with d3.js v${d.version}`;
if (
d.first &&
d.first !== 'N/A' &&
d.first !== ''
) {
html += ` and made a ${d.first}`;
}
if (d.favorite) {
html += `. ${_.upperFirst(d.possesivePronoun)} favorite d3 function is ${d.favorite}`;
}
html += '.';
const x = d.x + hexSize / 2;
const y = d.y + hexSize / 2;
hover.style('display', 'block')
.style('top', `${y}px`)
.style('left', `${x}px`)
.html(html);
}
function mouseleave() {
hover.style('display', 'none');
}
</script>
</body>
First Name,Last Name,version,first,favorite,possesivePronoun
Alan,McLean,v1,Scatter plot,d3.timer,his
alanna,scott,v3,"Bar chart, Line chart",,her
Alastair,Dant,v1,Treemap,d3.forceSimulation,his
Alex,Macy,v3,Line chart,d3.transition,his
Amit,Patel,v1,N/A,d3.behavior.zoom,his
Andrew,Wong,v3,Radial plot,d3.scale,his
Angela,Pablo,v2,Scatter plot,,her
Anna,Smylie,v3,Bar chart,,her
Anna,Vital,v2,Tree,d3.line,her
Anuja,Verma,v3,Tree,d3.hierarchy,her
Bo,Ericsson,v3,Line chart,selection.data,his
Brad,Lyon,v2,Chord diagram,d3.selectAll,his
Brent,Cohn,v3,Bar plot,d3.forceSimulation,his
Brian,Bailey,v4,Bar chart,d3.voronoi,his
Casey,Haber,v3,Bar chart,d3.force,his
Chris,Polis,v3,Time series,d3.transition,his
christophe,viau,v1,Tooltip,d3.axis,his
Daniel,Overstreet,v3,Scatter plot,d3.nest,his
Daniel,Wolfe,v3,Map,d3.transition,his
David,Richards,v3,Force,d3.interpolate,his
David,Schnurr,v3,Charting library,d3.voronoi,his
David,Lin,v3,Bar chart,selection.data,his
Don,McCurdy,v3,Choropleth,d3.voronoi,his
Eric,Socolofsky,v3,Map,d3.transition,his
Erik,Cunningham,v3,Scatter plot,selection.data,his
Erik,Hazzard,v1,Radar chart,d3.voronoi,his
Francois,Chabbey,v3,Bar chart,d3.scale,his
Marcos,Iglesias,v3,Bar chart,d3.selectAll,his
Hourann,Bosci,v3,"Map, Bubble chart",d3.scaleOrdinal,his
Ian,Johnson,v1,Bar chart,d3.scaleLinear,his
Ivy,Wang,v1,Network graph,nest.rollup,her
James,Womack,v3,Scatter plot,d3.scale,his
James,Wenzel,v3,Line chart,nest.map,his
Jamie,Popkin,v2,Map,transform,his
Janine,Heiser,v3,Bar chart,d3.selection,her
Jay,Mahabal,v3,Bar chart,d3.scale,his
Jeff,Zerger,v2,Map,d3.geoProjection,his
Jeffrey,Catania,v2,Line chart,d3.voronoi,his
Jennifer,Lee,v3,Bar chart,d3.transition,her
Jeremy,Wong,v3,Line Chart,d3.geoProjection,his
Jeremy,Wildfire,v2,Bar chart,d3.scale,his
Jim,Vallandingham,v2,Bubble chart,d3.nest,his
Jing,Lu,v4,Scatter plot,d3.brush,her
John,Schulz,v2,Area chart,d3.histogram,his
John,Huynh,v3,Scatter plot,d3.transition,his
Jon,Sadka,v4,Bar chart,d3.transition,his
Jon,Nguyen,v2,Bar chart,d3.extent,his
Jonathan,Nesci,v2,Bar chart,scale.invert,his
Kai,Chang,v1,Chord diagram,d3.interpolateMagma,his
Ken,Penn,v3,Bar chart,d3.forceSimulation,his
Kerry,Rodden,v1,Chord diagram,d3.scale,her
Krist,Wongsuphasawat,v3,Scatter plot,"d3.selection, d3.scale",his
Kristen,Judd,v3,Pie chart,d3.transition,her
Kristin,Henry,v2,Chloropleth,d3.scale,her
Leland,Lee,v3,Bar chart,arcTween,his
Logan,Carter,v4,"Pie chart, Donut chart",d3.pie,his
Ludwig,Schubert,v3,Tree,projection.fitSize,his
Marie,Swarzenski,v3,Line chart,d3.transition,her
Mark,Vital,v2,Tree,d3.line,his
Matthew,Isanuk,v4,Scatter plot,d3.scale,his
Mauro,Perez,v3,Sunburst,selection.data,his
Micah,Stubbs,v3,Map,d3.request,his
Michael,Freeman,v2,Bar chart,d3.pack,his
Mike,Bostock,v1,N/A,d3.pack,his
Minwei,Xu,v4,bar chart,d3.layout.force,his
Naoya,Kanai,v3,bar chart,d3.scale,his
Noah,Veltman,v2,Line chart,d3.transition,his
Patrick,Sun,v3,Scatter Plot,selection.attr,his
Paul,Rosenzweig,v4,Scatter plot,d3.selectAll,his
Paul,Van slembrouck,v1,Bar chart,d3.scale,his
Philippe,Rivière,v3,Map,d3.geoProjection,his
Phillip,Mispagel,v4,Donut chart,"d3.scale, d3.zoom",his
Ramesh,Sampath,v4,Scatter plot,d3.scale,his
Ric,Cheng,v3,Line chart,d3.brush,his
Robert,Crocker,v3,Bar chart,d3.nest,his
Robert,Harris,v2,Force,d3.scale,his
Roger,Fischer,v3,Map,d3.geo,his
Rumman,Chowdhury,v3,Line chart,d3.voronoi,her
Saniya,Jesupaul,v3,Scatter plot,d3.color,her
Sara,Quigley,v2,Circle pack,d3.nest,her
Sara,Schnadt,v3,Scatter plot,,her
Sarah,Kwak,v4,Donut chart,d3.transition,her
Seemant,Kulleen,v3,Donut chart,d3.nest,his
Shan,Carter,v2,Bubble chart,d3.scaleLinear,his
Shelby,Sturgis,v3,Scatter plot,d3.scale,his
Shirley,Wu,v2,Tree,d3.forceSimulation,her
Siu-Mei,Man,v4,Bar chart,d3.voronoi,her
Susie,Lu,v2,Bar chart,d3.nest,her
Tarek,Rached,v2,Bar chart,d3.layout.pack,his
Tim Hyon,Hyon,v3,Parallel coordinates,d3.scale,his
Tony,Chu,v3,Bubble chart,d3.scale,his
Toshiyuki,Hayashi,v3,Network graph,"selection.enter, selection.exit",his
Vasco,Asturiano,v2,Force,selection.data,his
Victor,Powell,v4,Scatter plot,d3.geo.path,his
Visnu,Pitiyanuvath,v2,Bar chart,d3.nest,his
Xianlin,Hu,v3,Force,d3.transition,her
Yukiko,Steineman,v3,Bar chart,d3.color,her
Zack,DeSario,v2,Bar chart,selection.enter,his
var midpoint = -2/3;
var halfSide = Math.sqrt(1 / 3);
var triangle = [
(-halfSide) + ',-1',
(halfSide) + ',-1',
'0,0'
];
var favoritesScale = d3.scaleLinear()
.range([midpoint - .05, -.85]);
var versionsScale = d3.scaleOrdinal()
.range(_.rangeRight(3, 7));
var firstsScale = d3.scaleOrdinal();
var hexScale = 40;
var hexSize = hexScale * 2 / Math.sqrt(3);
d3.csv('data.csv', data => {
/**************************************
** process data and add them to scales as domain
**************************************/
var attendees = _.map(data, d => {
return {
name: d['First Name'],
first: d.first.split(', ')[0],
favorite: d.favorite.split(', ')[0],
version: parseInt(d.version.replace('v', '')),
};
});
var firsts = _.chain(attendees)
.map('first').flatten()
.countBy().toPairs()
.sortBy(d => -d[1])
.map(0).value();
var favorites = _.chain(attendees)
.map('favorite').flatten()
.countBy().value();
var versions = _.chain(attendees)
.map('version').flatten()
.countBy().toPairs()
.sortBy(d => -d[1])
.filter(d => d[1] > 1)
.map(0).value();
// update colors with number of classes
var colors = chroma.cubehelix()
.start(280)
// .rotations(-1 / firsts.length)
.gamma(0.5)
.lightness([0.3, 0.4])
.scale() // convert to chroma.scale
// .correctLightness()
.colors(firsts.length);
firstsScale.domain(firsts).range(colors);
versionsScale.domain(_.range(1, 5));
var maxFavorites = _.max(_.values(favorites));
favoritesScale.domain([1, maxFavorites]);
/**************************************
** legend
**************************************/
var baseLegendData = {
version: 1,
first: 'Bar chart',
favorite: 'd3.scale',
};
var legend = d3.select('#legend');
var annotation = d3.select('#annotation');
// first, the versions
// annotation.append('div')
// .style('position', 'absolute')
// .style('width', '1000px')
// .style('top', hexScale)
// .html('Which version of d3 did you start with?');
var versionsData = _.times(4, i => {
return Object.assign(_.clone(baseLegendData), {
version: i + 1,
legendText: 'd3 v' + (i + 1),
});
});
legend.append('g')
.attr('transform', 'translate(' + [0, 0] + ')')
.call(drawHexagons, versionsData, 4);
// first
// annotation.append('div')
// .style('position', 'absolute')
// .style('width', '1000px')
// .style('top', 5.5 * hexScale)
// .html('Your first d3 project:<br />what type of visualization was it?');
var firstData1 = _.times(4, i => {
var index = !i ? i : i * 4 - 1;
return Object.assign(_.clone(baseLegendData), {
first: firsts[index],
legendText: firsts[index],
});
});
var firstData2 = _.times(3, i => {
var index = (!i ? i : i * 4 - 1) + 14;
return Object.assign(_.clone(baseLegendData), {
first: firsts[index],
legendText: firsts[index],
});
});
legend.append('g')
.attr('transform', 'translate(' + [0, 4 * hexScale] + ')')
.call(drawHexagons, firstData1, 4);
legend.append('g')
.attr('transform', 'translate(' + [1.9 * hexScale, 6.75 * hexScale] + ')')
.call(drawHexagons, firstData2, 3);
// favorites
// annotation.append('div')
// .style('position', 'absolute')
// .style('width', '1000px')
// .style('top', 13.5 * hexScale)
// .html('What is your favorite d3 api function?');
var favoriteNames = ['d3.scale', 'd3.transition', 'd3.forceSimulation', 'd3.hierarchy'];
var favoriteData = _.times(4, i => {
return Object.assign(_.clone(baseLegendData), {
favorite: favoriteNames[i],
legendText: favoriteNames[i],
});
});
legend.append('g')
.attr('transform', 'translate(' + [0, 10.75 * hexScale] + ')')
.call(drawHexagons, favoriteData, 4);
/**************************************
** draw the hexagons
**************************************/
var perRow = 5;
var svg = d3.select('#flowers').append('g')
.call(drawHexagons, attendees, perRow);
function drawHexagons(selection, data, perRow) {
var hex = selection.selectAll('.hex')
.data(data).enter().append('g')
.classed('hex', true)
.attr('transform', (d, i) => {
var row = Math.floor(i / perRow);
var x = (i % perRow * 3.2 + 1.5) * hexSize;
if (row % 2) {
x += 1.6 * hexSize;
}
var y = (row * 1.05 + 2) * hexScale;
d.x = x;
d.y = y;
return 'translate(' + [x, y] + ')scale(' + hexScale + ')';
}).on('mouseenter', mouseenter)
.on('mouseleave', mouseleave);
var triangles = hex.selectAll('triangle')
.data((d, j) => {
return _.times(versionsScale(d.version), i => {
return {
angle: i * 60,
midpoint: favoritesScale(favorites[d.favorite]),
color: firstsScale(d.first),
// color: colors[j],
};
});
}).enter().append('g')
.classed('triangle', true)
.attr('transform', d => 'rotate(' + d.angle + ')');
triangles.append('polygon')
.attr('points', triangle.join(' '))
.attr('fill', d => chroma(d.color).darken(2.5));
// .attr('fill', '#fff');
triangles.selectAll('.subtri')
.data(d => _.times(3, i => {
return Object.assign({
subangle: i * 120,
subcolor: chroma(d.color).saturate(1.5).brighten(i),
}, d);
})).enter().append('polygon')
.classed('subtri', true)
.attr('transform', d => 'rotate(' + d.subangle + ' 0 ' + midpoint + ')')
.attr('points', d => {
return [
(-halfSide) + ',-1',
'0,' + d.midpoint,
halfSide + ',-1'
].join(' ');
}).attr('fill', d => d.subcolor);
// add in text if it's for legend
var fontSize = 14;
hex.filter(d => d.legendText)
.append('text')
.attr('y', 1.25)
.attr('font-size', fontSize / hexScale)
.attr('text-anchor', 'middle')
.attr('dy', '.35em')
.text(d => d.legendText);
}
});
/**************************************
** hover interaction
**************************************/
var hover = d3.select('#hover')
.style('display', 'none');
function mouseenter(d) {
if (!d.name) return;
var html = '<h2>' + d.name + '</h2>';
html += '<p>started with d3.js v' + d.version;
if (d.first) {
html += ' and made a ' + d.first;
}
if (d.favorite) {
html += '. Their favorite d3 function is ' + d.favorite;
}
html += '.';
var x = d.x + hexSize / 2;
var y = d.y + hexSize / 2;
hover.style('display', 'block')
.style('top', y + 'px')
.style('left', x + 'px')
.html(html);
}
function mouseleave() {
hover.style('display', 'none');
}
const midpoint = -2/3;
const halfSide = Math.sqrt(1 / 3);
const triangle = [
`${-halfSide},-1`,
`${halfSide},-1`,
'0,0'
];
const favoritesScale = d3.scaleLinear()
.range([midpoint - .05, -.85]);
const versionsScale = d3.scaleOrdinal()
.range(_.rangeRight(3, 7));
const firstsScale = d3.scaleOrdinal();
const hexScale = 40;
const hexSize = hexScale * 2 / Math.sqrt(3);
d3.csv('data.csv', data => {
/**************************************
** process data and add them to scales as domain
**************************************/
const attendees = _.map(data, d => {
return {
name: d['First Name'],
first: d.first.split(', ')[0],
favorite: d.favorite.split(', ')[0],
version: parseInt(d.version.replace('v', '')),
};
});
const firsts = _.chain(attendees)
.map('first').flatten()
.countBy().toPairs()
.sortBy(d => -d[1])
.map(0).value();
const favorites = _.chain(attendees)
.map('favorite').flatten()
.countBy().value();
const versions = _.chain(attendees)
.map('version').flatten()
.countBy().toPairs()
.sortBy(d => -d[1])
.filter(d => d[1] > 1)
.map(0).value();
// update colors with number of classes
const colors = chroma.cubehelix()
.start(280)
// .rotations(-1 / firsts.length)
.gamma(0.5)
.lightness([0.3, 0.4])
.scale() // convert to chroma.scale
// .correctLightness()
.colors(firsts.length);
firstsScale.domain(firsts).range(colors);
versionsScale.domain(_.range(1, 5));
const maxFavorites = _.max(_.values(favorites));
favoritesScale.domain([1, maxFavorites]);
/**************************************
** legend
**************************************/
const baseLegendData = {
version: 1,
first: 'Bar chart',
favorite: 'd3.scale',
};
const legend = d3.select('#legend');
const annotation = d3.select('#annotation');
// first, the versions
// annotation.append('div')
// .style('position', 'absolute')
// .style('width', '1000px')
// .style('top', hexScale)
// .html('Which version of d3 did you start with?');
const versionsData = _.times(4, i => {
return Object.assign(_.clone(baseLegendData), {
version: i + 1,
legendText: `d3 v${i + 1}`,
});
});
legend.append('g')
.attr('transform', `translate(${[0, 0]})`)
.call(drawHexagons, versionsData, 4);
// first
// annotation.append('div')
// .style('position', 'absolute')
// .style('width', '1000px')
// .style('top', 5.5 * hexScale)
// .html('Your first d3 project:<br />what type of visualization was it?');
const firstData1 = _.times(4, i => {
const index = !i ? i : i * 4 - 1;
return Object.assign(_.clone(baseLegendData), {
first: firsts[index],
legendText: firsts[index],
});
});
const firstData2 = _.times(3, i => {
const index = (!i ? i : i * 4 - 1) + 14;
return Object.assign(_.clone(baseLegendData), {
first: firsts[index],
legendText: firsts[index],
});
});
legend.append('g')
.attr('transform', `translate(${[0, 4 * hexScale]})`)
.call(drawHexagons, firstData1, 4);
legend.append('g')
.attr('transform', `translate(${[1.9 * hexScale, 6.75 * hexScale]})`)
.call(drawHexagons, firstData2, 3);
// favorites
// annotation.append('div')
// .style('position', 'absolute')
// .style('width', '1000px')
// .style('top', 13.5 * hexScale)
// .html('What is your favorite d3 api function?');
const favoriteNames = ['d3.scale', 'd3.transition', 'd3.forceSimulation', 'd3.hierarchy'];
const favoriteData = _.times(4, i => {
return Object.assign(_.clone(baseLegendData), {
favorite: favoriteNames[i],
legendText: favoriteNames[i],
});
});
legend.append('g')
.attr('transform', `translate(${[0, 10.75 * hexScale]})`)
.call(drawHexagons, favoriteData, 4);
/**************************************
** draw the hexagons
**************************************/
const perRow = 5;
const svg = d3.select('#flowers').append('g')
.call(drawHexagons, attendees, perRow);
function drawHexagons(selection, data, perRow) {
const hex = selection.selectAll('.hex')
.data(data).enter().append('g')
.classed('hex', true)
.attr('transform', (d, i) => {
const row = Math.floor(i / perRow);
let x = (i % perRow * 3.2 + 1.5) * hexSize;
if (row % 2) {
x += 1.6 * hexSize;
}
const y = (row * 1.05 + 2) * hexScale;
d.x = x;
d.y = y;
return `translate(${[x, y]})scale(${hexScale})`;
}).on('mouseenter', mouseenter)
.on('mouseleave', mouseleave);
const triangles = hex.selectAll('triangle')
.data((d, j) => {
return _.times(versionsScale(d.version), i => {
return {
angle: i * 60,
midpoint: favoritesScale(favorites[d.favorite]),
color: firstsScale(d.first),
// color: colors[j],
};
});
}).enter().append('g')
.classed('triangle', true)
.attr('transform', d => `rotate(${d.angle})`);
triangles.append('polygon')
.attr('points', triangle.join(' '))
.attr('fill', d => chroma(d.color).darken(2.5));
// .attr('fill', '#fff');
triangles.selectAll('.subtri')
.data(d => _.times(3, i => {
return Object.assign({
subangle: i * 120,
subcolor: chroma(d.color).saturate(1.5).brighten(i),
}, d);
})).enter().append('polygon')
.classed('subtri', true)
.attr('transform', d => `rotate(${d.subangle} 0 ${midpoint})`)
.attr('points', d => {
return [
`${-halfSide},-1`,
`0,${d.midpoint}`,
`${halfSide},-1`
].join(' ');
}).attr('fill', d => d.subcolor);
// add in text if it's for legend
const fontSize = 14;
hex.filter(d => d.legendText)
.append('text')
.attr('y', 1.25)
.attr('font-size', fontSize / hexScale)
.attr('text-anchor', 'middle')
.attr('dy', '.35em')
.text(d => d.legendText);
}
});
/**************************************
** hover interaction
**************************************/
const hover = d3.select('#hover')
.style('display', 'none');
function mouseenter(d) {
if (!d.name) return;
let html = `<h2>${d.name}</h2>`;
html += `<p>started with d3.js v${d.version}`;
if (d.first) {
html += ` and made a ${d.first}`;
}
if (d.favorite) {
html += `. Their favorite d3 function is ${d.favorite}`;
}
html += '.';
const x = d.x + hexSize / 2;
const y = d.y + hexSize / 2;
hover.style('display', 'block')
.style('top', `${y}px`)
.style('left', `${x}px`)
.html(html);
}
function mouseleave() {
hover.style('display', 'none');
}