Created
July 24, 2017 23:11
-
-
Save mglukhovsky/d6664666953c884c024a7665ac5c251b to your computer and use it in GitHub Desktop.
Realtime graph with D3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<title>Stripe Charts</title> | |
</head> | |
<style> | |
body { | |
font: 10px sans-serif; | |
.layer path, .layer line { | |
fill: none; | |
stroke: red; | |
} | |
.axis path, .axis line { | |
fill: none; | |
stroke: #000; | |
shape-rendering: crispEdges; | |
} | |
} | |
</style> | |
<svg width="960" height="500"></svg> | |
</html> | |
<body> | |
<!-- D3 --> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script> | |
<script src="/realtime-graph-d3.js"></script> | |
</body> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict' | |
// Generate random values | |
let month = new Date(2014, 0, 1) | |
function randomData() { | |
let newData = { | |
month, | |
apples: Math.random()*3000, | |
bananas: Math.random()*2000, | |
cherries: Math.random()*1000, | |
dates: Math.random()*400 | |
} | |
month = moment(month).add(30, 'days').toDate() | |
return newData | |
} | |
const data = [] | |
for (let i=0; i < 12; i++) { | |
data[i] = randomData() | |
} | |
const keys = ['apples', 'bananas', 'cherries', 'dates'] | |
// Get the maximum of the total number of event types | |
let maxEventCount = d3.max(data, (d) => { | |
// Sum the values for each event type | |
return d3.sum(keys, (key) => d[key])*1.5 | |
}) | |
const xDomain = () => { | |
return [data[0].month, data[data.length-2].month] | |
} | |
console.log(maxEventCount) | |
// Build the SVG plot | |
const margin = {top: 20, right: 20, bottom: 30, left: 50}, | |
width = 1000 - margin.left - margin.right, | |
height = 500 - margin.top - margin.bottom | |
const x = d3.scaleTime() | |
// Find the largest and smallest dates for the domain | |
.domain(xDomain()) | |
.range([0, width]), | |
y = d3.scaleLinear() | |
// Set the domain to the highest event count | |
.domain([0,maxEventCount]) | |
.range([height, 0]), | |
color = d3.scaleOrdinal(d3.schemeCategory10) | |
const svg = d3.select('svg') | |
.attr('width', width + margin.left + margin.right) | |
.attr('height', height + margin.top + margin.bottom) | |
.append('g') | |
.attr('transform', `translate(${margin.left},${margin.top})`) | |
svg.append('defs').append('clipPath') | |
.attr('id', 'clip') | |
.append('rect') | |
.attr('width', width) | |
.attr('height', height) | |
// Use a stack layout to create our stacked area graph | |
const stack = d3.stack() | |
.keys(keys) | |
.order(d3.stackOrderNone) | |
.offset(d3.stackOffsetNone) | |
const stackedData = stack(data) | |
// Build our area graph (using cardinal smoothing to create curves) | |
const area = d3.area() | |
.curve(d3.curveCardinal) | |
// The x axis is based on time | |
.x((d,i) => x(d.data.month)) | |
// y0 and y1 represent the bottom and top values for each section of the area graph | |
.y0((d) => y(d[0])) | |
.y1((d) => y(d[1])) | |
const xAxis = d3.axisBottom() | |
.scale(x) | |
const yAxis = d3.axisLeft() | |
.scale(y) | |
// Create a group layer for each event type | |
const layer = svg.selectAll('.layer') | |
.data(stackedData) | |
.enter().append('g') | |
.attr('class', 'layer') | |
.attr('clip-path', 'url(#clip)') | |
// Add the actual area paths for each event type | |
const path = layer.append('path') | |
.attr('class', 'areas') | |
.attr('d', area) | |
.style('fill', (d, i) => color(i)) | |
// Add our axes to the plot | |
const xAxisGroup = svg.append('g') | |
.attr('class', 'x axis') | |
.attr('transform', `translate(0, ${height})`) | |
.attr('clip-path', 'url(#clip)') | |
.call(xAxis) | |
svg.append('g') | |
.attr('class', 'y axis') | |
.call(yAxis) | |
tick() | |
function tick() { | |
data.push(randomData()) | |
x.domain(xDomain()) | |
d3.selectAll('.areas') | |
.data(stack(data)) | |
.attr('d', area) | |
.attr('transform', null) | |
const prevDate = moment(data[0].month).subtract(30, 'days').toDate() | |
console.log(d3.extent(data, d => d.month)) | |
let transitionCount = 0 | |
d3.selectAll('.areas') | |
.transition() | |
.duration(1000) | |
.ease(d3.easeLinear) | |
.attr('transform', `translate(${x(prevDate)})`) | |
.on('end', () => { | |
transitionCount += 1 | |
if (transitionCount === keys.length) { | |
xAxisGroup.transition() | |
.duration(1000) | |
.ease(d3.easeLinear) | |
.call(xAxis) | |
tick() | |
} | |
}) | |
data.shift() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment