|
<!DOCTYPE html> |
|
<script src="https://unpkg.com/d3@4.6.0"></script> |
|
<script src="https://unpkg.com/d3fc@12.1.0"></script> |
|
|
|
<style> |
|
body { |
|
font: 12px sans-serif; |
|
} |
|
.area { |
|
fill: #cec6b9; |
|
} |
|
#small-multiples > div { |
|
display: inline-block; |
|
height: 185px; |
|
width: 50% |
|
} |
|
@media (min-width: 600px) { |
|
#small-multiples > div { |
|
width: 33% |
|
} |
|
} |
|
@media (min-width: 800px) { |
|
#small-multiples > div { |
|
width: 25% |
|
} |
|
} |
|
@media (min-width: 1000px) { |
|
#small-multiples > div { |
|
width: 20% |
|
} |
|
} |
|
.tooltip .x-axis .tick { |
|
display: none; |
|
} |
|
.x-axis .domain, .x-axis .tick path, |
|
.y-axis .domain, .y-axis .tick path { |
|
display: none; |
|
} |
|
.plot-area { |
|
overflow: visible !important; |
|
} |
|
.x-axis { |
|
height: 1.5em !important; |
|
} |
|
.gridline-x { |
|
stroke: white; |
|
} |
|
.point { |
|
fill: black; |
|
} |
|
.point text { |
|
text-anchor: middle; |
|
transform: translateY(-10px); |
|
font-size: 10px; |
|
stroke: none; |
|
} |
|
.bottom-handle { |
|
text-anchor: middle; |
|
alignment-baseline: hanging; |
|
font-size: 10px; |
|
transform: translateY(1.5em); |
|
} |
|
.top-handle { |
|
display: none; |
|
} |
|
.annotation-line line { |
|
display: none; |
|
} |
|
</style> |
|
|
|
<div id='small-multiples'></div> |
|
|
|
<script> |
|
d3.tsv('askmefi_category_year.tsv') |
|
.row(r => ({category: r.category, n: Number(r.n), year: Number(r.year)})) |
|
.get(data => { |
|
var nested = d3.nest() |
|
.key(k => k.category) |
|
.entries(data); |
|
|
|
nested.forEach(g => { |
|
g.tip = []; |
|
}); |
|
|
|
var yExtent = fc.extentLinear() |
|
.accessors([function(d) { return d.n; }]) |
|
.pad([0, 0.2]) |
|
.include([0]); |
|
|
|
var xExtent = fc.extentLinear() |
|
.accessors([function(d) { return d.year; }]); |
|
|
|
var area = fc.seriesSvgArea() |
|
.crossValue(function(d) { return d.year; }) |
|
.mainValue(function(d) { return d.n; }); |
|
|
|
var line = fc.seriesSvgLine() |
|
.crossValue(function(d) { return d.year; }) |
|
.mainValue(function(d) { return d.n; }); |
|
|
|
var gridlines = fc.annotationSvgGridline() |
|
.xTicks(0) |
|
.yTicks(3); |
|
|
|
var point = fc.seriesSvgPoint() |
|
.crossValue(function(d) { return d.year; }) |
|
.mainValue(function(d) { return d.value; }) |
|
.size(25) |
|
.decorate(function(selection) { |
|
selection.enter() |
|
.append('text'); |
|
selection.select('text') |
|
.text(d => d.value) |
|
}) |
|
|
|
var line = fc.annotationSvgLine() |
|
.orient('vertical') |
|
.value(function(d) { return d.year; }) |
|
.decorate(function(selection) { |
|
selection.enter() |
|
.select('.bottom-handle') |
|
.append('text'); |
|
selection.select('.bottom-handle text') |
|
.text(d => d.year) |
|
}) |
|
|
|
|
|
var multi = fc.seriesSvgMulti() |
|
.series([area, line, gridlines, line, point]) |
|
.mapping((data, index, series) => { |
|
switch (series[index]) { |
|
case point: |
|
case line: |
|
return data.tip; |
|
default: |
|
return data.values; |
|
} |
|
}); |
|
|
|
var xScale = d3.scaleLinear(); |
|
// create a chart |
|
var chart = fc.chartSvgCartesian( |
|
xScale, |
|
d3.scaleLinear()) |
|
.yDomain(yExtent(data)) |
|
.xDomain(xExtent(data)) |
|
.xLabel(d => d.key) |
|
.yTicks(3) |
|
.xTicks(2) |
|
.xTickFormat(d3.format('0')) |
|
//.xTickValues([2004, 2014]) |
|
.yOrient('left') |
|
.plotArea(multi); |
|
|
|
function render() { |
|
// render |
|
var container = d3.select('#small-multiples') |
|
var update = container.selectAll('div.multiple') |
|
.data(nested); |
|
update.enter() |
|
.append('div') |
|
.classed('multiple', true) |
|
.merge(update) |
|
.call(chart) |
|
.classed('tooltip', d => d.tip.length); |
|
|
|
// add the pointer component to the plot-area, re-rendering |
|
// each time the event fires. |
|
var pointer = fc.pointer() |
|
.on('point', function(event) { |
|
// determine the year |
|
if (event.length) { |
|
var year = Math.round(xScale.invert(event[0].x)); |
|
// add the point to each series |
|
nested.forEach(group => { |
|
var value = group.values.find(v => v.year === year); |
|
group.tip = [{ |
|
year: year, |
|
value: value.n |
|
}]; |
|
}) |
|
} else { |
|
nested.forEach(g => { |
|
g.tip = []; |
|
}) |
|
} |
|
render(); |
|
}); |
|
|
|
d3.selectAll('#small-multiples .plot-area') |
|
.call(pointer); |
|
} |
|
|
|
render(); |
|
}); |
|
|
|
|
|
|
|
</script> |