Source: World Bank
Based on this visualization by NYTimes Graphics.
Uses D3 area to mask and hide particles above the line.
Particles: Andrew Mollica’s Floating Particles Area chart: d3noob's Simple graph with filled area in v4
gistup |
Based on this visualization by NYTimes Graphics.
Uses D3 area to mask and hide particles above the line.
Particles: Andrew Mollica’s Floating Particles Area chart: d3noob's Simple graph with filled area in v4
year | value | |
---|---|---|
1960 | 15.99977916 | |
1961 | 15.68125552 | |
1962 | 16.0139375 | |
1963 | 16.48276215 | |
1964 | 16.96811858 | |
1965 | 17.45172525 | |
1966 | 18.12107301 | |
1967 | 18.59831788 | |
1968 | 19.08938916 | |
1969 | 19.85794566 | |
1970 | 21.11125227 | |
1971 | 20.98020348 | |
1972 | 21.74864198 | |
1973 | 22.51058213 | |
1974 | 21.50293038 | |
1975 | 20.40222407 | |
1976 | 21.15761537 | |
1977 | 21.53248401 | |
1978 | 21.97300469 | |
1979 | 21.78043698 | |
1980 | 20.78648774 | |
1981 | 19.76676417 | |
1982 | 18.59049523 | |
1983 | 18.57154371 | |
1984 | 18.97675027 | |
1985 | 18.88231274 | |
1986 | 18.72072272 | |
1987 | 19.35033442 | |
1988 | 20.01041341 | |
1989 | 20.07576978 | |
1990 | 19.32275118 | |
1991 | 19.05616319 | |
1992 | 19.13943728 | |
1993 | 19.34708291 | |
1994 | 19.36089205 | |
1995 | 19.27654526 | |
1996 | 19.49602474 | |
1997 | 19.69036318 | |
1998 | 19.5792362 | |
1999 | 19.72716951 | |
2000 | 20.17875051 | |
2001 | 19.63650507 | |
2002 | 19.61340408 | |
2003 | 19.56410453 | |
2004 | 19.65837118 | |
2005 | 19.59188523 | |
2006 | 19.09406652 | |
2007 | 19.21789784 | |
2008 | 18.46176387 | |
2009 | 17.15773779 | |
2010 | 17.44216612 | |
2011 | 16.97345609 | |
2012 | 16.3042868 | |
2013 | 16.31435182 | |
2014 | 16.49060839 |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset='utf-8'> | |
<link rel='stylesheet' type='text/css' href='main.css'> | |
<script src='https://d3js.org/d3.v5.min.js'></script> | |
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.25.6/d3-legend.min.js'></script> | |
</head> | |
<body> | |
<div class='area'> | |
<div class='canvas'></div> | |
<svg class='chart'></svg> | |
</div> | |
</body> | |
<script src='index.js'></script> | |
</html> |
/* =============================================== */ | |
/* ================= AREA CHART ================== */ | |
/* =============================================== */ | |
function areaChart(container) { | |
var width = container.node().getBoundingClientRect().width - margin.left - margin.right; | |
var height = container.node().getBoundingClientRect().height - margin.top - margin.bottom; | |
// set the ranges | |
var x = d3.scalePoint().range([0, width]); | |
var y = d3.scaleLinear().range([height, 0]); | |
var areaChart = d3.area() | |
.x(function (d) { | |
return x(d.year); | |
}) | |
.y0(function (d) { | |
return y(d.value); | |
}) | |
.y1(0); | |
var valueline = d3.line() | |
.x(function (d) { | |
return x(d.year); | |
}) | |
.y(function (d) { | |
return y(d.value); | |
}); | |
svg = container.append('g') | |
.attr('transform', | |
'translate(' + margin.left + ',' + margin.top + ')'); | |
d3.csv('data.csv').then(function (data, error) { | |
if (error) throw error; | |
// format the data | |
data.forEach(function (d) { | |
d.year = +d.year; | |
d.value = +d.value; | |
}); | |
x.domain(data.map(d => d.year)) | |
y.domain([0, d3.max(data, function (d) { | |
return d.value; | |
}) + 1]); | |
// add the area | |
svg.append('path') | |
.data([data]) | |
.attr('class', 'area-chart') | |
.attr('fill', '#ffffff') | |
.attr('stroke', 'none') | |
.attr('d', areaChart); | |
var axisX = d3.axisBottom(x) | |
.tickSize(-height) | |
.tickValues(x.domain().filter(function (d, i) { | |
return !(i % 5) || i === x.domain(); | |
})) | |
.tickFormat(d3.format("d")) | |
var axisY = d3.axisRight(y) | |
.tickSize(-width) | |
.tickFormat(d3.format(".1f")); | |
svg.append('g') | |
.attr('class', 'axis x-axis') | |
.attr('stroke', '#999') | |
.attr('transform', 'translate(0,' + height + ')') | |
.call(axisX) | |
// move first tick label to the right so that it's fully visible | |
.select('.tick:first-of-type text') | |
.attr('transform', 'translate(' + x.step() + ',0)'); | |
var gAxisY = svg.append('g') | |
.attr('class', 'axis y-axis') | |
.attr('transform', 'translate(' + width + ',0)') | |
.call(axisY); | |
// remove 0.0 | |
gAxisY.select('.tick:first-of-type text') | |
.remove(); | |
gAxisY.append('text') | |
.attr('class', 'y-axis--label') | |
.attr('text-anchor', 'end') | |
.attr('transform', 'translate(27,-5)') | |
.text('Tons of CO₂ per capita'); | |
// add the valueline path. | |
svg.append('path') | |
.data([data]) | |
.attr('class', 'line-chart') | |
.attr('fill', 'none') | |
.attr('stroke', 'black') | |
.attr('d', valueline); | |
var max = d3.max(data, function (d) { | |
return d.value; | |
}); | |
var highlights = svg.selectAll('.highlight') | |
.data(data.filter(function (d, i) { | |
return i === 0 || i === data.length - 1 || d.value === max; | |
})) | |
.enter() | |
.append('g') | |
.attr('class', 'highlight'); | |
highlights.append('circle') | |
.attr('class', 'highlight__circle') | |
.attr('r', 6) | |
.attr('cx', function(d) { | |
return x(d.year); | |
}) | |
.attr('cy', function(d) { | |
return y(d.value); | |
}); | |
highlights.append('text') | |
.attr('class', 'highlight__text') | |
.attr('text-anchor', 'middle') | |
.attr('transform', function(d) { | |
return 'translate(' + x(d.year) + ',' + (y(d.value) - 10) + ')'; | |
}) | |
.text(function(d) { | |
return d.year; | |
}) | |
}); | |
} | |
/* =============================================== */ | |
/* ================= PARTICLES =================== */ | |
/* =============================================== */ | |
function particles(container) { | |
var width = container.node().getBoundingClientRect().width - margin.left - margin.right; | |
var height = container.node().getBoundingClientRect().height - margin.top - margin.bottom; | |
var scale = { | |
x: d3.scaleLinear().domain([0, 1]).range([0, width]), | |
y: d3.scaleLinear().domain([0, 1]).range([height, 0]), | |
}; | |
var canvas = container.append('canvas') | |
.attr('width', width) | |
.attr('height', height) | |
.style('margin-top', margin.top); | |
var context = canvas.node().getContext('2d'); | |
var particles = d3.range(5000) | |
.map(function () { | |
return { | |
x: Math.random(), | |
y: Math.random(), | |
t: Math.random() * 2 * Math.PI | |
}; | |
}); | |
draw(particles); | |
setInterval(function () { | |
particles = tick(particles); | |
draw(particles); | |
}, 66); | |
function tick(particles) { | |
return particles.map(function (d) { | |
d.t += Math.random() * .5 - .25; | |
d.x += .001 * Math.cos(d.t); | |
d.y += .001 * Math.sin(d.t); | |
if (d.x < 0 || d.x > width) { | |
d.x = .5; | |
} | |
if (d.y < 0 || d.y > height) { | |
d.y = .5; | |
} | |
return d; | |
}); | |
}; | |
var r = 0.8; | |
function draw(particles) { | |
context.clearRect(0, 0, width, height); | |
particles.forEach(function (d) { | |
var x = scale.x(d.x); | |
var y = scale.y(d.y); | |
context.beginPath() | |
context.arc(x, y, r, 0, 2 * Math.PI); | |
context.fillstyle = '#ddd'; | |
context.fill(); | |
}); | |
}; | |
} | |
var area = d3.select('.area'); | |
var divCanvas = area.select('.canvas'); | |
var svgChart = area.select('.chart'); | |
var margin = { | |
top: 40, | |
right: 50, | |
bottom: 40, | |
left: 20 | |
}; | |
var width = area.node().getBoundingClientRect().width; | |
var height = area.node().getBoundingClientRect().height; | |
svgChart.attr('width', width); | |
svgChart.attr('height', height); | |
divCanvas.attr('width', width); | |
divCanvas.attr('height', height); | |
divCanvas.style('padding-top', '' + margin.top + 'px'); | |
divCanvas.style('padding-left', '' + margin.left + 'px'); | |
particles(divCanvas); | |
areaChart(svgChart); |
.area { | |
position: absolute; | |
top: 0; | |
bottom: 0; | |
left: 0; | |
right: 0; | |
} | |
.canvas, | |
.chart { | |
position: absolute; | |
top: 0; | |
bottom: 0; | |
left: 0; | |
right: 0; | |
} | |
.axis text { | |
fill: #999; | |
stroke: none; | |
font: 300 12px / 1.2 "nyt-franklin", arial, helvetica, sans-serif; | |
shape-rendering: crispEdges; | |
text-rendering: optimizeLegibility; | |
-webkit-font-smoothing: antialiased; | |
} | |
.x-axis path.domain { | |
stroke: rgba(0,0,0,0); | |
} | |
.y-axis path.domain { | |
stroke: #ddd; | |
} | |
.axis line { | |
stroke: #ddd; | |
stroke-width: 1px; | |
} | |
.line-chart path { | |
opacity: 1; | |
stroke: #000000; | |
} | |
.highlight__text { | |
fill: #999; | |
stroke: none; | |
font: 400 18px / 1.2 "nyt-franklin", arial, helvetica, sans-serif; | |
shape-rendering: crispEdges; | |
text-rendering: optimizeLegibility; | |
-webkit-font-smoothing: antialiased; | |
} |