Skip to content

Instantly share code, notes, and snippets.

@milroc
Last active December 19, 2015 17:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save milroc/5988531 to your computer and use it in GitHub Desktop.
Save milroc/5988531 to your computer and use it in GitHub Desktop.
basis interpolation + circles

This example is showcasing an issue with syncing other representations of data (to see hover over a chart to see a circle) on a non-linear (e.g: basis) line or area chart.

<!DOCTYPE html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="http://littlesparkvt.com/flatstrap/assets/css/bootstrap.css"/>
<link type="text/css" rel="stylesheet" href="style.css"/>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="src.js"></script>
</head>
<body>
<div id="chart-1"></div>
<div id="chart-2"></div>
<div id="chart-3"></div>
<div id="chart-4"></div>
<div id="chart-5"></div>
<div id="chart-6"></div>
<div id="chart-7"></div>
<div id="chart-8"></div>
<div id="chart-9"></div>
<div id="chart-10"></div>
<div id="chart-11"></div>
<div id="chart-12"></div>
<script type="text/javascript">
var parseDate = d3.time.format("%b %Y").parse;
var colors = ["#88E48E",
"#EBAACD",
"#E9B859",
"#7CD2EC",
"#F0A487",
"#D4E264",
"#70E2B8",
"#BCB8E8",
"#B9DB95",
"#84DCD3",
"#D0C578",
"#C5CCDB"];
function randomDate(start, end) {
return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime()))
}
var start = new Date(2008, 0, 0),
end = new Date(),
dates = d3.range(200).map(function(d, i) {
return {
x: randomDate(start, end)
};
}).sort(function(a, b) { return a.x - b.x; });
var j = 0;
function random(y) {
++j;
return dates.map(function(d, i) {
return {
x: d.x,
y: Math.random() * y,
i: 'chart-' + j
};
});
}
var line = d3.custom.area()
.y(d3.scale.linear().domain([0, 120]))
.x(d3.time.scale().domain([start, end]))
.height(120)
.width(900)
.interpolation('basis')
.yAxis(d3.svg.axis().ticks(4))
.xAxis(false)
.title('chart-' + i)
// .line(false)
// .area(false)
.darken(0);
d3.select('#chart-1')
.datum(random(100))
.call(line
.colors(colors[0])
.title('chart-' + 1));
for (var i = 2; i <= 12; i++) {
var line = d3.custom.area()
.y(d3.scale.linear().domain([0, 120]))
.x(d3.time.scale().domain([start, end]))
.height(120)
.width(900)
.interpolation('basis')
.yAxis(d3.svg.axis().ticks(4))
.xAxis(false)
.title('chart-' + i)
// .line(false)
// .area(false)
.darken(0);
d3.select('#chart-'+i)
.datum(random(100))
.call(line.colors(colors[i])
.title('chart-' + i));
}
</script>
</body>
</html>
date price
Jan 2000 200
Feb 2000 200
Mar 2000 200
Apr 2000 200
May 2000 200
Jun 2000 200
Jul 2000 1000
Aug 2000 1000
Sep 2000 1000
Oct 2000 1000
Nov 2000 1000
Dec 2000 1000
Jan 2001 1000
Feb 2001 1000
Mar 2001 1000
Apr 2001 200
May 2001 200
Jun 2001 200
Jul 2001 200
Aug 2001 200
Sep 2001 200
Oct 2001 200
Nov 2001 200
Dec 2001 200
Jan 2002 200
Feb 2002 200
Mar 2002 200
Apr 2002 200
May 2002 200
Jun 2002 200
Jul 2002 200
Aug 2002 200
Sep 2002 200
Oct 2002 200
Nov 2002 200
Dec 2002 200
Jan 2003 200
Feb 2003 200
Mar 2003 200
Apr 2003 200
May 2003 200
Jun 2003 200
Jul 2003 200
Aug 2003 200
Sep 2003 200
Oct 2003 200
Nov 2003 200
Dec 2003 200
Jan 2004 200
Feb 2004 200
Mar 2004 200
Apr 2004 200
May 2004 200
Jun 2004 200
Jul 2004 200
Aug 2004 200
Sep 2004 200
Oct 2004 200
Nov 2004 200
Dec 2004 200
Jan 2005 200
Feb 2005 200
Mar 2005 200
Apr 2005 200
May 2005 200
Jun 2005 200
Jul 2005 200
Aug 2005 200
Sep 2005 200
Oct 2005 200
Nov 2005 200
Dec 2005 200
Jan 2006 200
Feb 2006 200
Mar 2006 200
Apr 2006 200
May 2006 200
Jun 2006 200
Jul 2006 200
Aug 2006 200
Sep 2006 200
Oct 2006 200
Nov 2006 200
Dec 2006 200
Jan 2007 200
Feb 2007 200
Mar 2007 200
Apr 2007 200
May 2007 200
Jun 2007 200
Jul 2007 200
Aug 2007 200
Sep 2007 200
Oct 2007 200
Nov 2007 200
Dec 2007 200
Jan 2008 200
Feb 2008 200
Mar 2008 200
Apr 2008 200
May 2008 200
Jun 2008 200
Jul 2008 200
Aug 2008 200
Sep 2008 200
Oct 2008 200
Nov 2008 200
Dec 2008 200
Jan 2009 200
Feb 2009 200
Mar 2009 200
Apr 2009 200
May 2009 200
Jun 2009 200
Jul 2009 200
Aug 2009 200
Sep 2009 200
Oct 2009 200
Nov 2009 200
Dec 2009 200
Jan 2010 200
Feb 2010 200
Mar 2010 200
date price
Jan 2000 200
Feb 2000 200
Mar 2000 200
Apr 2000 200
May 2000 200
Jun 2000 200
Jul 2000 1000
Aug 2000 1000
Sep 2000 1000
Oct 2000 1000
Nov 2000 1000
Dec 2000 1000
Jan 2001 1000
Feb 2001 1000
Mar 2001 1000
Apr 2001 200
May 2001 200
Jun 2001 200
Jul 2001 200
Aug 2001 200
Sep 2001 200
Oct 2001 200
Nov 2001 200
Dec 2001 200
Jan 2002 200
Feb 2002 200
Mar 2002 200
Apr 2002 200
May 2002 200
Jun 2002 200
Jul 2002 200
Aug 2002 200
Sep 2002 200
Oct 2002 200
Nov 2002 200
Dec 2002 200
Jan 2003 200
Feb 2003 200
Mar 2003 200
Apr 2003 200
May 2003 200
Jun 2003 200
Jul 2003 1200
Aug 2003 1200
Sep 2003 1200
Oct 2003 1200
Nov 2003 1200
Dec 2003 1200
Jan 2004 1200
Feb 2004 1200
Mar 2004 1200
Apr 2004 1200
May 2004 1200
Jun 2004 1200
Jul 2004 1200
Aug 2004 1200
Sep 2004 1200
Oct 2004 1200
Nov 2004 1200
Dec 2004 1200
Jan 2005 1200
Feb 2005 1200
Mar 2005 1200
Apr 2005 1200
May 2005 1200
Jun 2005 1200
Jul 2005 1200
Aug 2005 1200
Sep 2005 1200
Oct 2005 1200
Nov 2005 1200
Dec 2005 1200
Jan 2006 1200
Feb 2006 1200
Mar 2006 1200
Apr 2006 1200
May 2006 1200
Jun 2006 1200
Jul 2006 1200
Aug 2006 1200
Sep 2006 1200
Oct 2006 1200
Nov 2006 1200
Dec 2006 1200
Jan 2007 1200
Feb 2007 1200
Mar 2007 1200
Apr 2007 1200
May 2007 1200
Jun 2007 1200
Jul 2007 1200
Aug 2007 200
Sep 2007 200
Oct 2007 200
Nov 2007 200
Dec 2007 200
Jan 2008 200
Feb 2008 200
Mar 2008 200
Apr 2008 200
May 2008 200
Jun 2008 200
Jul 2008 200
Aug 2008 200
Sep 2008 200
Oct 2008 200
Nov 2008 200
Dec 2008 200
Jan 2009 200
Feb 2009 200
Mar 2009 200
Apr 2009 200
May 2009 200
Jun 2009 200
Jul 2009 200
Aug 2009 200
Sep 2009 200
Oct 2009 200
Nov 2009 200
Dec 2009 200
Jan 2010 200
Feb 2010 200
Mar 2010 200
date price
Jan 2000 1394.46
Feb 2000 1366.42
Mar 2000 1498.58
Apr 2000 1452.43
May 2000 1420.6
Jun 2000 1454.6
Jul 2000 1430.83
Aug 2000 1517.68
Sep 2000 1436.51
Oct 2000 1429.4
Nov 2000 1314.95
Dec 2000 1320.28
Jan 2001 1366.01
Feb 2001 1239.94
Mar 2001 1160.33
Apr 2001 1249.46
May 2001 1255.82
Jun 2001 1224.38
Jul 2001 1211.23
Aug 2001 1133.58
Sep 2001 1040.94
Oct 2001 1059.78
Nov 2001 1139.45
Dec 2001 1148.08
Jan 2002 1130.2
Feb 2002 1106.73
Mar 2002 1147.39
Apr 2002 1076.92
May 2002 1067.14
Jun 2002 989.82
Jul 2002 911.62
Aug 2002 916.07
Sep 2002 815.28
Oct 2002 885.76
Nov 2002 936.31
Dec 2002 879.82
Jan 2003 855.7
Feb 2003 841.15
Mar 2003 848.18
Apr 2003 916.92
May 2003 963.59
Jun 2003 974.5
Jul 2003 990.31
Aug 2003 1008.01
Sep 2003 995.97
Oct 2003 1050.71
Nov 2003 1058.2
Dec 2003 1111.92
Jan 2004 1131.13
Feb 2004 1144.94
Mar 2004 1126.21
Apr 2004 1107.3
May 2004 1120.68
Jun 2004 1140.84
Jul 2004 1101.72
Aug 2004 1104.24
Sep 2004 1114.58
Oct 2004 1130.2
Nov 2004 1173.82
Dec 2004 1211.92
Jan 2005 1181.27
Feb 2005 1203.6
Mar 2005 1180.59
Apr 2005 1156.85
May 2005 1191.5
Jun 2005 1191.33
Jul 2005 1234.18
Aug 2005 1220.33
Sep 2005 1228.81
Oct 2005 1207.01
Nov 2005 1249.48
Dec 2005 1248.29
Jan 2006 1280.08
Feb 2006 1280.66
Mar 2006 1294.87
Apr 2006 1310.61
May 2006 1270.09
Jun 2006 1270.2
Jul 2006 1276.66
Aug 2006 1303.82
Sep 2006 1335.85
Oct 2006 1377.94
Nov 2006 1400.63
Dec 2006 1418.3
Jan 2007 1438.24
Feb 2007 1406.82
Mar 2007 1420.86
Apr 2007 1482.37
May 2007 1530.62
Jun 2007 1503.35
Jul 2007 1455.27
Aug 2007 1473.99
Sep 2007 1526.75
Oct 2007 1549.38
Nov 2007 1481.14
Dec 2007 1468.36
Jan 2008 1378.55
Feb 2008 1330.63
Mar 2008 1322.7
Apr 2008 1385.59
May 2008 1400.38
Jun 2008 1280
Jul 2008 1267.38
Aug 2008 1282.83
Sep 2008 1166.36
Oct 2008 968.75
Nov 2008 896.24
Dec 2008 903.25
Jan 2009 825.88
Feb 2009 735.09
Mar 2009 797.87
Apr 2009 872.81
May 2009 919.14
Jun 2009 919.32
Jul 2009 987.48
Aug 2009 1020.62
Sep 2009 1057.08
Oct 2009 1036.19
Nov 2009 1095.63
Dec 2009 1115.1
Jan 2010 1073.87
Feb 2010 1104.49
Mar 2010 1140.45
d3.custom = {};
d3.custom.utils = {};
d3.custom.utils.windowResize = function() {};
d3.custom.area = function() {
// basic data
var margin = {top: 4, bottom: 20, left: 20, right: 50},
width = 2500,
height = 200,
// accessors
xValue = function(d) { return new Date(+d.x); },
yValue = function(d) { return +d.y; },
colorValue = function(d) { return d.color; },
// chart underpinnings
x = d3.time.scale(),
y = d3.scale.linear().ticks(2),
colors = d3.scale.category20(),
// axis
xAxis = d3.svg.axis(),
yAxis = d3.svg.axis(),
// chart enhancements
elastic = {
x: false,
y: false
},
zeroed = {
x: false,
y: true
},
convertData = true, // TODO fix to allow this to be exposed
darken = 0,
line = d3.svg.line(),
area = d3.svg.area(),
duration = 500,
bisector = null,
title = '',
interpolation = "linear",
formatNumber = d3.format(',f')
formatTime = d3.time.format("%Y-%m-%d"),
context = this;
function render(selection) {
// TODO realize that all the chartEnter stuff won't work if someone changes
// something to enable an x-axis
selection.each(function(data) {
// setup the basics
var w = width - margin.left - margin.right,
h = height - margin.top - margin.bottom;
// data
if (convertData) {
// TODO realize that lambda d[i]; only applies to converted data
data = data.map(function(d, i) {
return [xValue.call(data, d, i),
parseFloat(yValue.call(data, d, i))]; // why parseFloat here
});
}
var bisect = d3.bisector(function(d) { return d[0]; }).left;
// scales
if (elastic.x) {
var extent = d3.extent(data, function(d) { return d[0]; });
if (zeroed.x) extent[0] = 0;
x.domain(extent);
}
if (elastic.y) {
var extent = d3.extent(data, function(d) { return d[1]; });
if (zeroed.y) extent[0] = 0;
y.domain(extent);
}
x.rangeRound([0, w], .1);
y.range([h, 0]);
var svg = selection.selectAll('svg').data([data]),
svgEnter = svg.enter();
var cEnter = svgEnter.append('svg')
.attr('width', width)
.attr('height', height);
var chartEnter = cEnter.append('g')
.attr('width', w)
.attr('height', h)
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
.classed('d3-custom-chart', true);
// TODO handle the variables better
cEnter.append('text')
.attr('class', 'title');
var chart = svg.select('.d3-custom-chart');
if (xAxis) {
xAxis.scale(x).orient('bottom');
chartEnter.append('g')
.classed('x axis', true)
.attr('transform', 'translate(' + 0 + ',' + h + ')');
chart.select('.x.axis')
.transition()
.duration(duration)
.call(xAxis);
}
if (yAxis) {
yAxis.scale(y).orient('right');
chartEnter.append('g')
.attr('transform', 'translate(' + w + ',' + 0 + ')')
.classed('y axis', true);
chart.select('.y.axis')
.transition()
.duration(duration)
.call(yAxis);
}
// line and area helpers
if (area) {
area.interpolate(interpolation)
.x(function(d) { return x(d[0]); })
.y0(h)
.y1(function(d) { return y(d[1]); });
chart.append('path')
.attr('d', area(data))
.style('fill', function(d) { return d3.hsl(colors(d[1])); })
.classed('area', true);
}
if (line) {
line.interpolate(interpolation)
.x(function(d) { return x(d[0]); })
.y(function(d) { return y(d[1]); });
chart.append('path')
.attr('d', line(data))
.style('stroke', function(d) { return d3.hsl(colors(d[1])).darker(darken); })
.classed('line', true);
}
if (title) {
svg.select('.title')
.attr('transform', 'translate(' + (w - title.length * 6) + ',' + (h - 8) + ')')
.text(title);
}
chart.append('g')
.attr('class', 'focus')
.style('display', 'none');
var focus = svg.select('.focus');
focus.append('circle')
.attr('stroke', colors())
.attr('r', 4.5);
focus.append('text')
.attr('x', 9)
.attr('dy', '.35em');
svg.append('rect')
.attr('width', w)
.attr('height', h)
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
.classed('overlay', true)
.on("mouseover", function() { focus.style("display", null); })
.on("mouseout", function() { focus.style("display", "none"); })
.on("mousemove", mousemove);
// TODO handle logic for preventing focus SVG clipping
// TODO figure out why colors() spits out the last other color used here
function mousemove() {
var xPixelSpace = d3.mouse(this)[0],
x0 = x.invert(xPixelSpace),
i = bisect(data, x0),
d0 = data[i - 1],
d1 = data[i],
d = (x0 - d0[0] > d1[0] - x0) ? d1 : d0;
focus.attr("transform", "translate(" + x(d[0]) + "," + y(d[1]) + ")");
focus.select("text").text(formatTime(d[0]) + ': ' + formatNumber(d[1])); // TODO overall focus so we can remove date
console.log(xPixelSpace, x0, i, d0, d1, d);
}
});
}
// basic data
render.margin = function(_) {
if (!arguments.length) return margin;
margin = _;
return render;
};
render.width = function(_) {
if (!arguments.length) return width;
width = _;
return render;
};
render.height = function(_) {
if (!arguments.length) return height;
height = _;
return render;
};
// accessors
render.xValue = function(_) {
if (!arguments.length) return xValue;
xValue = _;
return render;
};
render.yValue = function(_) {
if (!arguments.length) return yValue;
yValue = _;
return render;
};
// scales
render.x = function(_) {
if (!arguments.length) return x;
x = _;
return render;
};
render.y = function(_) {
if (!arguments.length) return y;
y = _;
return render;
};
render.colors = function(_) {
if (!arguments.length) return colors;
colors = d3.functor(_);
return render;
};
render.title = function(_) {
if (!arguments.length) return title;
title = _;
return render;
};
// axis
render.xAxis = function(_) {
if (!arguments.length) return xAxis;
xAxis = _;
return render;
};
render.yAxis = function(_) {
if (!arguments.length) return yAxis;
yAxis = _;
return render;
};
// chart underpinnings
render.line = function(_) {
if (!arguments.length) return line;
line = _;
return render;
};
render.area = function(_) {
if (!arguments.length) return area;
area = _;
return render;
};
render.interpolation = function(_) {
if (!arguments.length) return interpolation;
interpolation = _;
return render;
};
// render.elastic = function(_) {
//
// }
render.darken = function(_) {
if (!arguments.length) return darken;
darken = _;
return render;
};
// cloning
render.clone = function() {
var that = this;
var temp = function temporary() { return that.apply(this, arguments); };
for( key in this ) {
temp[key] = this[key];
}
return temp;
};
return render;
};
body {
font: 14px helvetica;
color: #f0f0f0;
background-color: #3E4651;
}
.row {
padding: 5px;
}
.y.axis line,
.y.axis path {
fill: none;
stroke: #FFF;
shape-rendering: crispEdges;
}
.x.axis line,
.x.axis path {
fill: none;
stroke: none;
}
.axis text {
fill: #FFF;
}
.brush .extent {
stroke: #FFF;
fill-opacity: .125;
shape-rendering: crispEdges;
}
.area {
opacity: .6;
}
.line {
fill: none;
/*shape-rendering: crispEdges;*/
}
.title {
fill: #FFF;
stroke: none;
}
.focus circle {
fill: none;
stroke-width: 2px;
}
.focus text {
fill: #FFF;
}
.overlay {
fill: none;
pointer-events: all;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment