var datastorm = datastorm || {}; datastorm.rain = (function(){ var my = {}; var wrapper = d3.select('#wrapper'); var width = wrapper.node().clientWidth; var height = wrapper.node().clientHeight; var raindrops = []; var minMonthlyRainfall, maxMonthlyRainfall; var startTs; // start time of animation. Used to calculate elapsed time var previousTs; var tsStep; // time since the previous frame var durationTs = 400000; // full length of animation var animationActive = false; var doLabels = false; var nextActiveRaindropIndex = 0; var makeRaindropActiveInterval; // map radius to opacity var opacityScale = d3.scale.linear().domain([200, 0]).range([1, 0]).clamp(true); // var startTime = Date.now(); var colour = d3.scale.linear().domain([-1, 5, 16, 22]).range(['lightblue', 'white', 'orange', 'red']).clamp(true); // map the rainfall amount to how quickly the opacity reduces var circleOpacityDampingScale = d3.scale.linear(); // map the rainfall amount to how quickly the radius reduces var circleStartOpacityScale = d3.scale.linear(); var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; var ctx = datastorm.canvas.ctx; function initialiseData(rainJson, tempJson) { // Create array of raindrops _.each(rainJson, function(year) { // console.log(year); _.each(year.rainfall, function(rainfall, i) { if(rainfall === -1) return; raindrops.push({ rainfall: rainfall, radius: 1, active: false, labelDone: false, // opacity: 1, x: Math.random() * width, y: Math.random() * height, date: months[i] + ' ' + year.year }); }); }); // Create array of temperature var temps = []; _.each(tempJson, function(year) { _.each(year.temp, function(t) { temps.push(t); }); }); // Merge in temperature _.each(raindrops, function(raindrop, i) { raindrop.temp = temps[i]; raindrop.colour = colour(raindrop.temp); }); minMonthlyRainfall = _.min(raindrops, function(d) {return d.rainfall;}).rainfall; maxMonthlyRainfall = _.max(raindrops, function(d) {return d.rainfall;}).rainfall; circleOpacityDampingScale.domain([minMonthlyRainfall, maxMonthlyRainfall]).range([1000, 8000]); circleStartOpacityScale.domain([minMonthlyRainfall, maxMonthlyRainfall]).range([0.3, 1]); _.each(raindrops, function(raindrop) { raindrop.opacityDamping = 6000; //circleOpacityDampingScale(raindrop.rainfall); raindrop.opacity = circleStartOpacityScale(raindrop.rainfall); }); // console.log(raindrops); } function makeRaindropActive() { if(nextActiveRaindropIndex >= raindrops.length) return; raindrops[nextActiveRaindropIndex].active = true; nextActiveRaindropIndex += 1; } function doFrame() { tsStep = Date.now() - previousTs; previousTs = Date.now(); updateRaindropAttributes(); ctx.globalAlpha = 1; ctx.fillStyle = "rgba(0, 0, 0, 0.5)"; ctx.fillRect(0, 0, width, height); render(); var elapsedTs = Date.now() - startTs; if(elapsedTs > durationTs) animationActive = false; if(animationActive) window.requestAnimationFrame(doFrame); } function updateRaindropAttributes() { _.each(raindrops, function(rd) { if(!rd.active) return; // Update radius rd.radius += tsStep / 20; // Reduce opacity rd.opacity -= tsStep / rd.opacityDamping; if(rd.opacity < 0) { rd.opacity = 0; rd.active = false; } }); } function render() { // console.log('render'); ctx.fillStyle = 'none'; ctx.lineWidth = 4; _.each(raindrops, function(rd) { if(!rd.active) return; opacityScale.range([rd.opacity, 0]); for(var r = rd.radius; r > 5; r -= 25) { ctx.strokeStyle = rd.colour; ctx.globalAlpha = opacityScale(r); datastorm.canvas.drawCircleOutline(rd.x, rd.y, r); } if(doLabels && rd.labelDone === false) { // console.log('label', rd.x, rd.y) ctx.fillStyle = rd.colour; ctx.font = "14px sans-serif"; ctx.globalAlpha = rd.opacity - 0.2; ctx.fillText(rd.date, rd.x - 30, rd.y + 5); // rd.labelDone = true; ctx.fillStyle = 'none'; } }); } my.init = function() { d3.json('https://d28qoto45d39ov.cloudfront.net/datastorm/visualisations/rain/data/rainfall.json', function(err, rainJson) { d3.json('https://d28qoto45d39ov.cloudfront.net/datastorm/visualisations/rain/data/maxtemp.json', function(err, tempJson) { initialiseData(rainJson, tempJson); }); }); }; my.start = function() { animationActive = true; startTs = Date.now(); makeRaindropActiveInterval = setInterval(makeRaindropActive, 500); window.requestAnimationFrame(doFrame); }; my.stop = function() { animationActive = false; clearInterval(makeRaindropActiveInterval); } return my; }()); datastorm.rain.init(); datastorm.rain.start();
See the Pen Datastorm - Rain by Genevieve Smith-Nunes (@readysaltedcode) on CodePen.