Skip to content

Instantly share code, notes, and snippets.

@mthh
Last active April 26, 2020 10:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mthh/c8f7c673414da8980474a7bea569bb48 to your computer and use it in GitHub Desktop.
Save mthh/c8f7c673414da8980474a7bea569bb48 to your computer and use it in GitHub Desktop.
d3 transition
license: MIT
height: 535
Country CountryCode Series SeriesCode 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013
European Union EUU Adjusted net enrolment rate, primary, female (%) SE.PRM.TENR.FE 96.61009 96.59614 96.62024 96.41632 96.55341 96.50732 96.60095 96.34062 96.26472 96.0699 95.82447 95.21122 95.10601 94.84984 94.96997 95.10299 95.70635 95.6916 95.74489 95.59834 95.34953 95.22209 94.7738 95.11806 95.63038 96.0977 96.63364 97.36443 98.46769 98.3779 98.51014 98.54619 98.3794 98.26918 97.98124 98.23621 98.34876 98.47068 98.6939 98.78753 98.85742 98.78684 98.6698 98.32635
Fragile and conflict affected situations FCS Adjusted net enrolment rate, primary, female (%) SE.PRM.TENR.FE 41.26276 41.16214 42.05738 43.60742 43.30613 44.19519 45.5206 46.77219 47.65314 50.07585 52.68497 54.72917 53.7965 54.28538 54.52158 54.04974 54.70665 53.78846 54.32845 52.91621 50.82418 54.58232 53.65904 53.85171 54.44489 54.44947 53.13934 52.40899 52.19064 51.66156 52.20496 53.80738 56.71934 59.00989 61.13658 61.8487 63.68402 64.53225 67.52052 68.06396 68.88713 69.75518 71.90069 71.63384
Heavily indebted poor countries (HIPC) HPC Adjusted net enrolment rate, primary, female (%) SE.PRM.TENR.FE .. .. 31.94414 32.90021 33.68679 35.46441 37.50372 39.21024 40.68446 41.9581 43.74801 45.03222 44.22897 45.01984 44.92422 44.77224 45.07152 44.41191 44.60369 43.39967 42.19297 43.74316 42.58519 42.3936 43.36206 44.38504 44.15921 46.98458 47.98826 48.50737 50.39618 53.20747 56.95739 60.08622 62.95804 65.53953 68.02163 69.95456 73.25144 73.76343 74.33972 75.61815 76.91965 77.49571
Sub-Saharan Africa (all income levels) SSF Adjusted net enrolment rate, primary, female (%) SE.PRM.TENR.FE .. .. .. .. .. 39.10568 40.15078 42.60246 45.23686 48.4128 50.73162 52.91822 54.53045 54.9287 54.37809 53.12834 51.51788 50.42196 49.83144 50.21799 49.67436 50.82698 50.51715 51.02866 51.31942 51.34877 49.89618 52.69448 54.14818 55.38012 57.43951 59.3815 60.99001 63.49616 65.53115 67.84841 69.8376 71.88173 73.21121 73.86698 74.21368 75.07751 75.93007 76.29307
World WLD Adjusted net enrolment rate, primary, female (%) SE.PRM.TENR.FE 64.39105 63.8871 64.87696 67.83868 68.82677 70.6355 71.58348 71.51174 69.78469 70.03184 71.34842 72.21853 72.59067 72.87325 73.31925 74.38668 75.7264 76.29259 77.24019 77.69612 77.94786 78.17473 77.86134 78.17906 78.5283 78.23202 78.31261 78.79151 79.79754 81.211 82.26044 82.91992 83.56383 85.80512 86.43092 86.92009 87.76219 89.13018 89.59027 89.86923 90.19236 90.40838 90.43876 90.33611
OECD members OED Adjusted net enrolment rate, primary, female (%) SE.PRM.TENR.FE .. 89.93604 89.993 90.44101 90.24625 90.32824 90.91474 91.23871 91.41878 92.17577 94.09777 94.08079 94.66681 94.41048 94.77431 94.74189 95.6062 96.07206 96.41088 96.12245 96.80986 96.58962 96.18679 95.60099 95.45323 95.69942 95.68604 96.02915 96.58329 97.1593 97.43097 97.69493 97.20884 96.92627 96.51848 96.69753 97.13136 97.48092 97.65628 97.40247 97.09942 97.00714 96.80648 96.6306
Middle East & North Africa (all income levels) MEA Adjusted net enrolment rate, primary, female (%) SE.PRM.TENR.FE 44.85906 44.80407 47.79441 49.59928 50.69423 54.31628 57.05165 59.10183 60.84321 59.7132 63.03837 62.56794 64.45899 65.59448 67.14591 68.07089 69.5574 71.08415 72.47347 72.89762 75.48452 77.1297 73.92113 74.75731 77.89115 76.38415 80.01185 80.22886 80.62006 81.10303 82.22133 84.62012 86.60936 87.34724 88.44492 88.43019 89.35341 89.73569 90.53626 91.36755 92.99107 93.4294 94.33016 93.14842
Least developed countries: UN classification LDC Adjusted net enrolment rate, primary, female (%) SE.PRM.TENR.FE 29.3064 27.6506 31.29247 35.05885 34.77636 37.84784 40.15469 42.48859 43.4944 44.67606 46.23828 46.47017 45.35596 45.46051 46.05662 45.30972 45.5818 45.25263 48.17532 48.04211 47.65607 49.22526 49.14884 49.30633 50.20394 50.95232 51.05746 52.87445 53.61564 53.99038 56.3315 58.62957 62.35045 65.39041 68.40326 70.86196 73.43089 74.56947 76.73217 77.86371 77.86893 78.61898 79.55802 79.80053
Latin America & Caribbean (all income levels) LCN Adjusted net enrolment rate, primary, female (%) SE.PRM.TENR.FE 81.87537 82.24406 82.64448 83.23603 83.73576 83.75583 84.30577 85.17431 85.61146 87.13352 87.47275 88.59305 88.70893 88.86177 88.89106 89.23534 89.29865 89.47688 89.2616 88.42326 89.12177 89.4416 90.23628 90.46078 90.00822 89.89283 90.11073 90.26453 91.693 92.69359 93.22232 93.31715 94.17305 94.12511 94.49837 94.34318 94.3625 94.39295 94.07306 94.42434 94.53109 94.38145 93.75903 93.60884
High income HIC Adjusted net enrolment rate, primary, female (%) SE.PRM.TENR.FE .. 91.43513 91.13899 91.30447 90.8492 91.06884 91.65998 91.88216 92.31251 92.49517 94.4352 94.08515 94.36195 93.72985 93.42314 93.66524 94.40037 95.10331 95.49145 95.58638 95.66042 95.5905 94.96875 94.58119 94.46948 94.65624 95.02925 95.47564 95.93902 96.12058 96.29323 96.82125 96.34979 96.06647 95.81528 95.62739 96.1971 96.60526 96.94215 97.01588 96.80425 96.69625 96.62188 96.48698
East Asia & Pacific (all income levels) EAS Adjusted net enrolment rate, primary, female (%) SE.PRM.TENR.FE .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. 89.26129 93.49796 94.98887 94.1821 94.05041 92.84168 91.56869 90.47326 89.55572 88.7911 88.69199 88.69633 91.54781 94.29917 94.53937 94.93492 95.14224 95.16641 94.90849 94.34484 94.05987 95.11704 95.42956 95.74127 96.04546 96.32802 95.83425 95.87639
Arab World ARB Adjusted net enrolment rate, primary, female (%) SE.PRM.TENR.FE 42.84292 42.82264 44.16319 45.35383 45.93243 47.77129 49.40708 50.58012 51.66248 53.91799 55.50587 56.1497 56.94808 57.94749 59.26525 60.05314 61.34173 62.89461 63.99371 64.15573 65.30609 66.56963 62.65608 63.52473 67.39799 65.9064 69.88704 70.57265 71.65497 73.15838 74.32056 75.98818 77.18511 77.79193 78.41859 78.71033 79.64724 79.75937 80.86663 81.29138 82.64643 82.74064 84.47501 83.4875
Europe & Central Asia (all income levels) ECS Adjusted net enrolment rate, primary, female (%) SE.PRM.TENR.FE .. .. .. 92.72244 92.67544 92.73936 93.0773 93.01079 92.95091 92.98602 92.68179 91.82266 91.86256 91.75669 92.74555 92.90579 93.57895 93.68688 94.24011 94.10122 94.30686 94.77744 94.53947 94.10468 93.93649 93.78877 94.16484 94.23135 94.96886 95.28114 95.99717 96.8206 97.281 96.96585 97.03667 96.39124 96.62284 96.75377 97.07202 96.90757 97.28002 97.37261 97.34016 97.23212
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>bars</title>
<link href="https://fonts.googleapis.com/css?family=Anton&display=swap" rel="stylesheet">
<style>
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis--y path {
display: none;
}
.bar {
fill-opacity: .9;
}
.wld {
fill: #9ecae1;
}
.chart-title {
margin-bottom: 0;
}
.source {
margin-top: 0;
font-size: 11px;
}
.year {
font-size: 32px;
left: 850px;
position: absolute;
top: 325px;
font-family: 'Anton', sans-serif;
letter-spacing: 3px;
}
</style>
</head>
<body>
<div class="wrapper">
<h4 class="chart-title">Adjusted net enrolment rate, primary, female (%) <span class="year"></span></h4>
<div class="chart"></div>
<p class="source">Data source: The World Bank</p>
<div>
<div>
<input type="radio" id="chknodelay" name="opt" value="nodelay"/>
<label for="chknodelay">No delay</label>
</div>
<div>
<input type="radio" id="chkdelay" name="opt" value="delay"/>
<label for="chkdelay">Delay between each element</label>
</div>
<div>
<input type="radio" id="chkmovebardelay" name="opt" value="movebardelay"/>
<label for="chkmovebardelay">Move bar first + delay between each element</label>
</div>
</div>
</div>
<script src="//d3js.org/d3.v5.min.js"></script>
<script>
const colors = {
'European Union': '#8dd3c7',
'Fragile and conflict affected situations': '#ffffb3',
'Heavily indebted poor countries (HIPC)': '#bebada',
'Sub-Saharan Africa (all income levels)': '#fb8072',
'World': '#b15928',
'OECD members': '#fdb462',
'Middle East & North Africa (all income levels)': '#b3de69',
'Least developed countries: UN classification': '#fccde5',
'Latin America & Caribbean (all income levels)': '#d9d9d9',
'High income': '#bc80bd',
'East Asia & Pacific (all income levels)': '#ccebc5',
'Arab World': '#ffed6f',
'Europe & Central Asia (all income levels)': '#80b1d3',
};
const margin = {top: 20, right: 60, bottom: 40, left: 260};
const width = 960 - margin.left - margin.right;
const height = 400 - margin.top - margin.bottom;
const percentFormat = d3.format('.0%');
const leftPadding = 5;
const delay = function(d, i) {
return i * 40;
};
function sortData(data) {
return data.sort((a, b) => b.value - a.value);
}
function removeGeoAreasWithNoData(data) {
return data.filter(d => d.value);
}
function prepareData(data) {
return data.reduce((accumulator, d) => {
Object.keys(d).forEach((k) => {
if (!Number.isInteger(+k)) { return; }
let value;
if (d[+k] === '..') {
value = 0;
} else {
value = +d[+k] / 100;
}
const newEntry = {
value,
geoCode: d.CountryCode,
geoName: d.Country,
};
if (accumulator[+k]) {
accumulator[+k].push(newEntry);
} else {
accumulator[+k] = [newEntry];
}
});
return accumulator;
}, {});
}
function xAccessor(d) {
return d.value;
}
function yAccessor(d) {
return d.geoName;
}
const xScale = d3.scaleLinear()
.range([0, width])
.domain([0, 1]);
const yScale = d3.scaleBand()
.rangeRound([0, height], 0.1)
.padding(0.1);
function drawXAxis(el) {
el.append('g')
.attr('class', 'axis axis--x')
.attr('transform', `translate(${leftPadding},${height})`)
.call(d3.axisBottom(xScale).tickFormat(percentFormat));
}
function drawYAxis(el, data, t) {
let axis = el.select('.axis--y');
if (axis.empty()) {
axis = el.append('g')
.attr('class', 'axis axis--y');
}
axis.transition(t)
.call(d3.axisLeft(yScale))
.selectAll('g')
.delay(behavior === 'nodelay' ? null : delay);
}
function drawBars(el, data, t) {
let barsG = el.select('.bars-g');
if (barsG.empty()) {
barsG = el.append('g')
.attr('class', 'bars-g');
}
const bars = barsG
.selectAll('.bar')
.data(data, yAccessor);
bars.exit()
.remove();
bars.enter()
.append('rect')
.attr('class', d => d.geoCode === 'WLD' ? 'bar wld' : 'bar')
.attr('width', d => xScale(xAccessor(d)))
.attr('x', leftPadding)
.attr('y', 300)
.style('fill', d => colors[yAccessor(d)])
if (behavior === 'delay') {
bars
.merge(bars).transition()
.duration(900)
.attr('y', d => yScale(yAccessor(d)))
.attr('height', yScale.bandwidth())
.attr('width', d => xScale(xAccessor(d)))
.delay(delay);
} else if (behavior === 'movebardelay') {
bars
.merge(bars).transition()
.duration(450)
.attr('y', d => yScale(yAccessor(d)))
.attr('height', yScale.bandwidth())
.delay(delay)
.transition()
.duration(450)
.attr('width', d => xScale(xAccessor(d)))
.delay(delay);
} else {
bars
.merge(bars).transition()
.duration(900)
.attr('y', d => yScale(yAccessor(d)))
.attr('height', yScale.bandwidth())
.attr('width', d => xScale(xAccessor(d)))
}
const labels = barsG
.selectAll('text')
.data(data, yAccessor);
labels.exit()
.remove();
labels.enter()
.append('text')
.attr('x', d => xScale(xAccessor(d)) + 7.5)
.attr('y', 300)
.style('fill', 'transparent')
.html(d => xAccessor(d))
if (behavior === 'delay') {
labels
.merge(labels)
.transition()
.duration(900)
.attr('y', d => yScale(yAccessor(d)) + 17.5)
.tween('text', function(d) {
const i = d3.interpolate(this.textContent, d.value);
const prec = (d.value + "").split(".");
return function a(t) {
this.textContent = Math.round(i(t) * 10000) / 10000;
};
})
.attr('x', d => xScale(xAccessor(d)) + 7.5)
.style('fill', 'black')
.delay(delay);
} else if (behavior === 'movebardelay') {
labels
.merge(labels)
.transition()
.duration(450)
.attr('y', d => yScale(yAccessor(d)) + 17.5)
.delay(delay)
.transition()
.duration(450)
.tween('text', function(d) {
const i = d3.interpolate(this.textContent, d.value);
const prec = (d.value + "").split(".");
return function a(t) {
this.textContent = Math.round(i(t) * 10000) / 10000;
};
})
.attr('x', d => xScale(xAccessor(d)) + 7.5)
.style('fill', 'black')
.delay(delay);
} else {
labels
.merge(labels)
.transition()
.duration(900)
.attr('y', d => yScale(yAccessor(d)) + 17.5)
.tween('text', function(d) {
const i = d3.interpolate(this.textContent, d.value);
const prec = (d.value + "").split(".");
return function a(t) {
this.textContent = Math.round(i(t) * 10000) / 10000;
};
})
.attr('x', d => xScale(xAccessor(d)) + 7.5)
.style('fill', 'black')
}
}
let behavior = 'nodelay';
const onchangeradio = function onchangeradio() {
if (this.checked) {
behavior = this.value;
}
};
document.querySelectorAll('input[name="opt"]')
.forEach((el) => {
el.onchange = onchangeradio;
});
document.querySelector('input[value="nodelay"]').checked = true;
const svg = d3.select('.chart').append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
fetch('./data.csv')
.then((res) => res.text())
.then((res) => {
const data = prepareData(d3.csvParse(res));
const years = Object.keys(data).map(d => +d);
const lastYear = years[years.length - 1];
let startYear = 1986; // years[0];
let selectedData = removeGeoAreasWithNoData(sortData(data[startYear]));
let geoAreas = selectedData.map(yAccessor);
d3.select('.year').text(startYear);
yScale.domain(geoAreas);
drawXAxis(svg, selectedData);
drawYAxis(svg, selectedData);
drawBars(svg, selectedData);
const interval = d3.interval(() => {
const t = d3.transition().duration(900);
startYear += 1;
selectedData = removeGeoAreasWithNoData(sortData(data[startYear]));
d3.select('.year').text(startYear);
yScale.domain(selectedData.map(yAccessor));
drawYAxis(svg, selectedData, t);
drawBars(svg, selectedData, t);
if (startYear === lastYear) {
interval.stop();
}
}, 1600);
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment