Based on Gapminder Subset, data and design by Gapminder, of course.
Not your cleanest code.
xxxxxxxxxx
<head>
<meta charset="utf-8">
<link href="https://fonts.googleapis.com/css?family=Quicksand" rel="stylesheet">
<title>My tribute to Hans Rosling</title>
<style>
body{
font-family: 'Quicksand', sans-serif;
}
text {
font-family: 'Quicksand', sans-serif;
font-size: 12px;
}
text.seventeen {
font-size: 300px;
fill: #e1e1e1;
letter-spacing: 30px;
}
text.fourtyseven {
font-size: 32px;
letter-spacing: 10px;
fill: #e1e1e1;
}
text.rosling {
font-size: 140px;
fill: #000000;
}
text.thanks {
font-size: 50px;
fill: #000000;
}
.dot {
stroke: #000;
opacity: 1;
}
.dot2 {
opacity: 1;
}
.axis path, .axis line {
fill: none;
stroke: rgb(153, 153, 153);
shape-rendering: crispEdges;
}
.y.label, .x.label {
font-size: 20px;
}
g.tick text, .label {
fill: rgb(153, 153, 153);
}
button {
border-radius: 40px;
font-size: 1em;
width: 40px;
height: 40px;
background: #fff;
display: block;
border: 0;
padding: 0;
margin: 5px;
cursor: pointer;
}
</style>
</head>
<body>
<button id="animate">
<svg class="vzb-icon vzb-icon-play" viewBox="3 3 42 42" xmlns="https://www.w3.org/2000/svg">
<path xmlns="https://www.w3.org/2000/svg" d="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm-4 29V15l12 9-12 9z"></path>
</svg>
</button>
<div id="chart"></div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
// Chart dimensions.
var margin = {top: 19.5, right: 32, bottom: 40, left: 32},
width = 960 - margin.right - margin.left,
height = 600 - margin.top - margin.bottom;
var colorscheme = ["#ff5872", "#7feb00", "#00d5e9", "#ffe700"];
// Create the SVG container and set the origin.
var 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 + ")");
// Various scales. These domains make assumptions of data
var xScale = d3.scaleLog().domain([400, 82000]).range([0, width]),
yScale = d3.scaleLinear().domain([20, 85]).range([height, 0]),
radiusScale = d3.scaleSqrt().domain([0, 5e8]).range([3, 40]),
colorScale = d3.scaleOrdinal(colorscheme);
// The x & y axes
var xAxis = d3.axisBottom(xScale)
.tickValues([500, 1000, 2000, 4000, 8000, 16000, 32000, 64000, 128000])
.tickFormat(function (d) {
if ((d / 10000) >= 1) {
d = d / 1000 + "k";
}
return d;
})
.tickSize(0),
yAxis = d3.axisLeft(yScale)
.tickValues([20, 30, 40, 50, 60, 70, 80])
.tickSize(0);
// Add the x-axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the y-axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
// Add an x-axis title
svg.append("text")
.attr("class", "x label")
.attr("text-anchor", "end")
.attr("x", width)
.attr("y", height + 32)
.text("Income per person, GDP/capita in $/year adjusted for inflation & prices");
// Add a y-axis title
svg.append("text")
.attr("class", "y label")
.attr("y", -20)
.attr("x", -22)
.attr("dy", ".75em")
.text("Life expectancy (years)");
svg.append("text")
.attr("class", "rosling")
.attr("text-anchor", "middle")
.attr("y", 3*height/4 - 100)
.attr("x", width/2)
.text("Hans Rosling")
.style("opacity", 0);
svg.append("text")
.attr("class", "yearlabel seventeen")
.attr("text-anchor", "middle")
.attr("y", 3*height/4 - 30)
.attr("x", width/2)
.text("2017");
svg.append("text")
.attr("class", "yearlabel fourtyseven")
.attr("text-anchor", "middle")
.attr("y", 3*height/4 - 30)
.attr("x", 3*width/4 - 5)
.text("1948 - ")
.style("opacity", 0);
svg.append("text")
.attr("class", "thanks")
.attr("text-anchor", "middle")
.attr("y", height - 70)
.attr("x", width/2)
.text("Thank you, professor. Rest in peace.")
.style("opacity", 0);
var dropPath = 'M 243.44676,222.01677 C 243.44676,288.9638 189.17548,343.23508 122.22845,343.23508 C 55.281426,343.23508 1.0101458,288.9638 1.0101458,222.01677 C 1.0101458,155.06975 40.150976,142.95572 122.22845,0.79337431 C 203.60619,141.74374 243.44676,155.06975 243.44676,222.01677 z';
d3.select("#animate").on("click", function(){
d3.select(".seventeen")
.transition().delay(4000).duration(6000)
.attr("x", 3*width/4 + 135)
.style("font-size", "32px")
.style("letter-spacing", "10px");
d3.select(".fourtyseven")
.transition().delay(10000).duration(2000)
.style("opacity", 1);
d3.select(".rosling")
.transition().delay(10000).duration(2000)
.style("opacity", 1);
d3.select(".thanks")
.transition().delay(12000).duration(2000)
.style("opacity", 1);
d3.selectAll(".dot")
.transition()
.delay(function(d,i) { return i*100; })
.style("opacity", 0);
d3.selectAll(".dot2").transition().duration(4000)
.delay(function(d,i) { return i*100; })
.attr("cy", function() {return Math.random()*100; })
.style("opacity", 0.1);
d3.selectAll(".drop")
.transition().duration(4000).delay(function(d,i) { return i*100; })
.style("opacity", 1)
.attr("transform", function(d, i){
return 'translate('+[xScale(+d.gdp) - radiusScale(+d.pop)*1.2, height+50] + ') scale('+radiusScale(+d.pop)*0.01+','+ radiusScale(+d.pop)*0.01+')';})
});
// Load the data
d3.queue()
.defer(d3.json, 'datapoints.json')
.defer(d3.json, 'geo.json')
.await(function(error, nations, geo) {
var nationsdata = [];
nations.rows.forEach(function(d) {
var obj = {};
obj.countrycode = d[0];
var geodata = geo.rows.filter(function(ccode){
return ccode[0] == d[0];
});
obj.name = geodata[0][1];
obj.region = geodata[0][2];
obj.lifeexp = d[3];
obj.gdp = d[2];
obj.pop = d[4];
nationsdata.push(obj);
return nationsdata;
});
var dotGroup2 = svg.append("g");
var dotGroup = svg.append("g");
var dots = dotGroup.selectAll("circle")
.data(nationsdata.sort(order));
var dots2 = dotGroup2.selectAll("circle")
.data(nationsdata.sort(order));
dots2
.enter().append("circle")
.attr("class", "dot2")
.attr("id", function(d){ return d.countrycode; })
.attr("cx", function(d) { return xScale(+d.gdp); })
.attr("cy", function(d) { return yScale(+d.lifeexp); })
.attr("r", function(d) { return radiusScale(+d.pop); })
.style("fill", function(d) { return colorScale(d.region); })
.append("title");
dots
.enter().append("circle")
.attr("class", "dot")
.attr("id", function(d){ return d.countrycode; })
.attr("cx", function(d) { return xScale(+d.gdp); })
.attr("cy", function(d) { return yScale(+d.lifeexp); })
.attr("r", function(d) { return radiusScale(+d.pop); })
.style("fill", function(d) { return colorScale(d.region); })
.append("title");
svg.selectAll('g.drop').data(nationsdata.sort(order))
.enter().append('g')
.attr("class", "drop")
.attr("transform", function(d, i){
return 'translate('+[xScale(+d.gdp) - radiusScale(+d.pop)*1.2, yScale(+d.lifeexp)- radiusScale(+d.pop)*1.2]+') scale('+radiusScale(+d.pop)*0.01+','+ radiusScale(+d.pop)*0.01+')';
})
.style("opacity", 0)
.append('path').attr("d", dropPath)
.style("fill", "none")
.style("stroke", "#000000")
.style("stroke-width", 10);
// Defines a sort order so that the smallest dots are drawn on top.
function order(a, b) {
return +b.pop - +a.pop;
}
});
</script>
</body>
Modified http://d3js.org/d3.v4.min.js to a secure url
https://d3js.org/d3.v4.min.js