Skip to content

Instantly share code, notes, and snippets.

@tuckergordon
Last active January 1, 2020 08:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tuckergordon/2affad2ea96535d01cb5ff54eb0aa528 to your computer and use it in GitHub Desktop.
Save tuckergordon/2affad2ea96535d01cb5ff54eb0aa528 to your computer and use it in GitHub Desktop.
D3 Particle Area Chart
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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment