Last active
August 29, 2015 14:25
-
-
Save m99coder/54d6e0130064c699e6e4 to your computer and use it in GitHub Desktop.
D3 Multiline Chart with Data Generator and Path Translation
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
/** | |
* Data Generator | |
*/ | |
var DataGenerator = (function() { | |
// number of series | |
var _numberOfSeries = 1; | |
// number of data points | |
var _numberOfDataPoints = 40; | |
// start at timestamp | |
var _startTimestampAt = new Date().getTime(); | |
// start at value | |
var _startValueAt = 50; | |
// incrementTo (i days by default) | |
var _incrementTo = function(c, i) { | |
var date = new Date(c); | |
date.setDate(date.getDate() + i); | |
return date.getTime(); | |
}; | |
// data array | |
var _data = []; | |
// create normal distribution of num numbers with mean and deviation | |
var _distribute = function(num, mean, deviation) { | |
return d3.range(num).map(d3.random.normal(mean, deviation)); | |
}; | |
// shift directions | |
var _SHIFT_TO_LEFT = -1; | |
var _SHIFT_TO_RIGHT = 1; | |
// shift data by num positions to direction (-1 for left, 1 for right) | |
var _shift = function(direction, num) { | |
for (var i=0, n=_data.length; i<n; i++) { | |
if (direction === _SHIFT_TO_LEFT) { | |
for (j=0; j<num; j++) { | |
var newValue = _data[i].shift(); | |
newValue.timestamp = _incrementTo(_data[i][_data[i].length - 1].timestamp, 1); | |
_data[i].push(newValue); | |
} | |
} | |
if (direction === _SHIFT_TO_RIGHT) { | |
for (j=0; j<num; j++) { | |
var newValue = _data[i].pop(); | |
newValue.timestamp = _incrementTo(_data[i][0].timestamp, -1); | |
_data[i].unshift(newValue); | |
} | |
} | |
} | |
}; | |
return { | |
/** | |
* getter/setter for number of series | |
* @param {number} num number of series to generate | |
* @returns {*} | |
*/ | |
numberOfSeries: function(num) { | |
if (typeof num === 'undefined') { | |
return _numberOfSeries; | |
} | |
_numberOfSeries = num; | |
return this; | |
}, | |
/** | |
* getter/setter for number of data points | |
* @param {number} num number of data points to generate | |
* @returns {*} | |
*/ | |
numberOfDataPoints: function(num) { | |
if (typeof num === 'undefined') { | |
return _numberOfDataPoints; | |
} | |
_numberOfDataPoints = num; | |
return this; | |
}, | |
/** | |
* getter/setter for start timestamp | |
* @param {number} start start timestamp | |
* @returns {*} | |
*/ | |
startTimestampAt: function(start) { | |
if (typeof start === 'undefined') { | |
return _startTimestampAt; | |
} | |
_startTimestampAt = start; | |
return this; | |
}, | |
/** | |
* getter/setter for start value | |
* @param {number} start start value | |
* @returns {*} | |
*/ | |
startValueAt: function(start) { | |
if (typeof start === 'undefined') { | |
return _startValueAt; | |
} | |
_startValueAt = start; | |
return this; | |
}, | |
/** | |
* getter/setter for increment to callback | |
* @param {function} inc increment callback | |
* @returns {*} | |
*/ | |
incrementTo: function(inc) { | |
if (typeof inc === 'undefined') { | |
return _incrementTo; | |
} | |
_incrementTo = inc; | |
return this; | |
}, | |
/** | |
* generate data | |
* @param {number} mean mean for normal distribution | |
* @param {number} deviation deviation for normal distribution | |
* @returns {DataGenerator} | |
*/ | |
generate: function(mean, deviation) { | |
var m = mean || 0; | |
var d = deviation || 0.2; | |
for (var i=0; i<_numberOfSeries; i++) { | |
_data[i] = []; | |
var distribution = d3.range(_numberOfDataPoints).map(d3.random.normal(m, d)); | |
for (var j=0; j<_numberOfDataPoints; j++) { | |
_data[i][j] = { | |
timestamp: _incrementTo(_startTimestampAt, j), | |
value: _startValueAt + distribution[j] | |
}; | |
} | |
} | |
return this; | |
}, | |
/** | |
* shift data points to the left by num positions | |
* @param {number} num number of positions to shift | |
*/ | |
shiftToLeft: function(num) { | |
_shift(_SHIFT_TO_LEFT, num); | |
return this; | |
}, | |
/** | |
* shift data points to the right by num positions | |
* @param {number} num number of positions to shift | |
*/ | |
shiftToRight: function(num) { | |
_shift(_SHIFT_TO_RIGHT, num); | |
return this; | |
}, | |
get: function() { | |
return _data; | |
} | |
}; | |
})(); |
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
<!doctype html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>Path Translations</title> | |
<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Roboto+Slab:400,100,300,700"> | |
<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Roboto:400,100,100italic,300,300italic,400italic,500,500italic,700,700italic,900,900italic"> | |
<link rel="stylesheet" type="text/css" href="style.css"> | |
</head> | |
<body> | |
<div id="line-chart"></div> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script> | |
<script src="data-generator.js"></script> | |
<script src="line-chart.js"></script> | |
<script> | |
// get data generator for 2 series | |
var dataGenerator = | |
DataGenerator | |
.numberOfSeries(2) | |
.startTimestampAt(new Date(2015, 0, 1).getTime()) | |
.generate(0, 10); | |
// get data | |
var data = dataGenerator.get(); | |
// get line chart | |
var lineChart = | |
LineChart | |
.config({ | |
id: '#line-chart' | |
}); | |
var updateChart = function() { | |
lineChart | |
.data(data) | |
.draw(function() { | |
// shift to left by one position | |
data = dataGenerator.shiftToLeft(1).get(); | |
updateChart(); | |
}); | |
}; | |
updateChart(); | |
</script> | |
</body> | |
</html> |
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
/** | |
* Line Chart | |
*/ | |
var LineChart = (function() { | |
// default configuration | |
var _DEFAULT_CONFIG = { | |
id: 'chart', | |
marginTop: 10, | |
marginRight: 10, | |
marginBottom: 10, | |
marginLeft: 10, | |
width: 600, | |
height: 140 | |
}; | |
// configuration | |
var _config = {}; | |
// scales | |
var _scales = {}; | |
// line handle | |
var _line = | |
d3.svg.line() | |
.interpolate('basis') | |
.x(function(d) { | |
return _scales.x(d.timestamp); | |
}) | |
.y(function(d) { | |
return _scales.y(d.value); | |
}); | |
// svg handle | |
var _svg = null; | |
// data array | |
var _data = []; | |
// get series range | |
// first and last element are cutted out to paint a more precise line | |
var _seriesRange = function(accessor) { | |
var seriesRanges = []; | |
_data.map(function(series) { | |
d3.extent(series.filter(function(d, i) { | |
return i > 0 && i < series.length - 1; | |
}), accessor).map(function(d) { | |
seriesRanges.push(d); | |
}); | |
}); | |
return d3.extent(seriesRanges); | |
}; | |
return { | |
/** | |
* getter/setter for configuration | |
* @param {object} config configuration | |
* @returns {*} | |
*/ | |
config: function(config) { | |
if (typeof config === 'undefined') { | |
return _config; | |
} | |
_config = config; | |
var id = _config.id || _DEFAULT_CONFIG.id; | |
var width = _config.width || _DEFAULT_CONFIG.width; | |
var height = _config.height || _DEFAULT_CONFIG.height; | |
var marginTop = _config.marginTop || _DEFAULT_CONFIG.marginTop; | |
var marginRight = _config.marginRight || _DEFAULT_CONFIG.marginRight; | |
var marginBottom = _config.marginBottom || _DEFAULT_CONFIG.marginBottom; | |
var marginLeft = _config.marginLeft || _DEFAULT_CONFIG.marginLeft; | |
// update scales | |
_scales.x = d3.time.scale().range([0, width]); | |
_scales.y = d3.scale.linear().range([height, 0]); | |
_scales.color = d3.scale.category10(); | |
// init svg | |
_svg = d3.select(id) | |
.append('svg') | |
.attr('width', width + marginLeft + marginRight) | |
.attr('height', height + marginTop + marginBottom) | |
.append('g') | |
.attr('transform', 'translate(' + marginLeft + ',' + marginRight + ')'); | |
// clipping path | |
_svg.append('defs').append('clipPath') | |
.attr('id', 'clip') | |
.append('rect') | |
.attr('width', width - marginLeft - marginRight) | |
.attr('height', height - marginTop - marginBottom) | |
.attr('x', marginLeft) | |
.attr('y', marginTop); | |
return this; | |
}, | |
/** | |
* getter/setter for data | |
* @param {array} data data | |
* @returns {*} | |
*/ | |
data: function(data) { | |
if (typeof data === 'undefined') { | |
return _data; | |
} | |
_data = data; | |
return this; | |
}, | |
/** | |
* draw chart | |
* @param {function} ready ready callback | |
* @returns {LineChart} | |
*/ | |
draw: function(ready) { | |
// update scale domains | |
_scales.x.domain(_seriesRange(function(d) { | |
return d.timestamp; | |
})); | |
/* | |
_scales.y.domain(_seriesRange(function(d) { | |
return d.value; | |
})); | |
*/ | |
_scales.y.domain([80, 20]); | |
_svg.selectAll('.series') | |
.data(data) | |
.enter() | |
.append('g') | |
.attr('clip-path', 'url(#clip)') | |
.attr('class', 'series') | |
.append('path') | |
.attr('class', 'line') | |
.attr('stroke', function(d, i) { | |
return _scales.color(i); | |
}); | |
_svg.selectAll('.line') | |
.attr('d', function(d) { | |
return _line(d); | |
}) | |
.attr('transform', null) | |
.transition() | |
.duration(950) | |
.ease('linear') | |
.attr('transform', function(d) { | |
return 'translate(' + _scales.x(d[0].timestamp) + ')'; | |
}) | |
.each('end', function(d, i) { | |
if (i === _data.length - 1) { | |
ready(); | |
} | |
}); | |
return this; | |
}, | |
/** | |
* get scales | |
* @returns {} | |
*/ | |
scales: function() { | |
return _scales; | |
} | |
}; | |
})(); |
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
* { | |
font-family: 'Roboto', sans-serif; | |
color: #666; | |
} | |
body { | |
margin: 20px 20px 20px 100px; | |
} | |
h1, h2, h3 { | |
font-family: 'Roboto Slab', serif; | |
color: #000; | |
} | |
.line { | |
fill: none; | |
stroke-width: 1.5px; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment