|
<!DOCTYPE html> |
|
|
|
<head> |
|
<!-- include polyfills for custom event and Symbol (for IE) --> |
|
<script src="https://unpkg.com/[email protected]/dist/polyfill.js"></script> |
|
<script src="https://unpkg.com/[email protected]/custom-event-polyfill.js"></script> |
|
|
|
<!-- use babel so that we can use arrow functions and other goodness in this block! --> |
|
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> |
|
|
|
<script src="https://unpkg.com/[email protected]"></script> |
|
<script src="https://unpkg.com/[email protected]"></script> |
|
|
|
<meta charset="utf-8" /> |
|
<style> |
|
body { |
|
font: 14px sans-serif; |
|
background-color: #363b3f; |
|
color: darkgrey; |
|
} |
|
|
|
.x-axis text { |
|
fill: darkgrey; |
|
} |
|
|
|
.x-axis .domain { |
|
display: none; |
|
} |
|
|
|
.x-axis .tick { |
|
font-size: 12px; |
|
} |
|
|
|
#chart { |
|
height: 480px; |
|
} |
|
|
|
.y-axis svg { |
|
display: none; |
|
} |
|
|
|
.language-label { |
|
text-anchor: end; |
|
font-size: 20px; |
|
fill: white; |
|
alignment-baseline: central |
|
} |
|
|
|
.language-percent { |
|
fill: darkgrey; |
|
alignment-baseline: central |
|
} |
|
|
|
.container { |
|
display: relative; |
|
} |
|
|
|
h2 { |
|
font-size: 30px; |
|
margin-bottom: 5px; |
|
} |
|
|
|
#date { |
|
color: darkgrey; |
|
font-size: 50px; |
|
position: absolute; |
|
bottom: 10px; |
|
right: 10px; |
|
} |
|
</style> |
|
</head> |
|
|
|
<h2>Most popular programming languages on StackOverflow</h2> |
|
<div class="container"> |
|
<div id="chart"></div> |
|
<div id="date"></div> |
|
</div> |
|
|
|
<script type="text/babel"> |
|
|
|
const months = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", |
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ]; |
|
|
|
d3.csv("data.csv").then(data => { |
|
// convert string properties to numbers |
|
data.forEach(d => { |
|
d.yr = Number(d.yr); |
|
d.mo = Number(d.mo); |
|
d.total = Number(d.total); |
|
}); |
|
|
|
// find all unique tags |
|
const tags = d3.set(data.map(d => d.tag)).values(); |
|
|
|
const colorScale = d3.scaleOrdinal(d3.schemeCategory10) |
|
.domain(tags); |
|
|
|
const percentFormat = d3.format(".1%"); |
|
|
|
// a D3FC bar series component https://d3fc.io/api/series-api.html |
|
const barSeries = fc |
|
.autoBandwidth(fc.seriesSvgBar()) |
|
.crossValue(d => d.tag) |
|
.align("left") |
|
.orient("horizontal") |
|
.key(d => d.tag) |
|
.mainValue(d => d.percent) |
|
.decorate(selection => { |
|
// this section uses the decorate pattern (https://d3fc.io/introduction/decorate-pattern.html) |
|
// to modify the data-join used by the bar series, allowing various customisations |
|
|
|
// colour each bar |
|
selection.enter().style("fill", d => colorScale(d.tag)); |
|
|
|
// add language and percent indicators |
|
selection |
|
.enter() |
|
.append("text") |
|
.classed("language-label", true) |
|
.attr("transform", "translate(-5, 0)") |
|
.text(d => d.tag); |
|
selection |
|
.enter() |
|
.append("text") |
|
.classed("language-percent", true) |
|
.attr("transform", "translate(5, 0)") |
|
selection.select(".language-percent") |
|
.text(d => percentFormat(d.percent)); |
|
}); |
|
|
|
// use D3FC extent (https://d3fc.io/api/extent-api.html) to compute |
|
// a suitable y axis range |
|
const yDomain = fc |
|
.extentLinear() |
|
.accessors([d => d.percent]) |
|
.include([0]) |
|
.pad([0.0, 0.05]); |
|
|
|
// the D3FC chart component (https://d3fc.io/api/chart-api.html) |
|
const chart = fc |
|
.chartCartesian(d3.scaleLinear(), d3.scaleBand()) |
|
.xOrient("top") |
|
.xTickFormat(percentFormat) |
|
.svgPlotArea(barSeries); |
|
|
|
const renderChart = (year, month) => { |
|
|
|
// filter the tag data for year / month and sort |
|
const currentTags = data |
|
.filter(d => d.yr === year && d.mo === month) |
|
.sort((a, b) => a.total - b.total); |
|
|
|
// compute the percentages |
|
const totalTagCount = d3.sum(currentTags, d => d.total); |
|
currentTags.forEach(d => (d.percent = d.total / totalTagCount)); |
|
|
|
// update the chart domain |
|
chart.yDomain(currentTags.map(d => d.tag)).xDomain(yDomain(currentTags)); |
|
|
|
// render the chart |
|
d3.select("#chart") |
|
.datum(currentTags) |
|
.transition() |
|
.ease(d3.easeLinear) |
|
.duration(700) |
|
.call(chart); |
|
|
|
// update the date indicator |
|
d3.select("#date") |
|
.text(`${months[month - 1]} ${year}`); |
|
}; |
|
|
|
let currentYear = 2009; |
|
let currentMonth = 1; |
|
|
|
// update the year / month on an interval timer |
|
const interval = setInterval(() => { |
|
renderChart(currentYear, currentMonth); |
|
currentMonth ++; |
|
if (currentMonth > 12) { |
|
currentMonth = 1; |
|
currentYear++; |
|
} |
|
if (currentMonth === 9 && currentYear === 2019) { |
|
clearInterval(interval); |
|
} |
|
}, 800); |
|
}); |
|
</script> |