D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
zachmargolis
Full window
Github gist
Line Comparison
<!DOCTYPE html> <html> <head> <title>WOO</title> <style> body { font: 10pt/12pt "Helvetica", sans-serif; } .axis path { fill: none; stroke: #555; shape-rendering: crispEdges; } .line { fill: none; stroke-width: 3px; } .line.a { stroke: #333; } .line.b { stroke: #888; } .area { stroke: none; fill: none; fill-opacity: 0.25; } .area.upper { fill: #0f0; } .area.lower { fill: #f00; } </style> </head> <body> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> <script type="text/javascript"> var n = 100, a = bumpLayer(n, .1), b = bumpLayer(n, .1); var margin = { top: 10, right: 10, bottom: 25, left: 50 }, width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var svg = d3.select('body') .append('svg') .attr('width', width + margin.left + margin.right) .attr('height', height + margin.top + margin.bottom); var x = d3.scale.linear() .domain([0, n]) .range([0, width]); var y = d3.scale.linear() .domain([0, d3.max(a.concat(b), function(d) { return d.y })]) .range([height, 0]); var xAxis = d3.svg.axis() .orient('bottom') .scale(x); var xAxisElem = svg.append('g') .attr('class', 'x axis') .attr('transform', translate(margin.left, height + margin.top + 5)); xAxisElem.call(xAxis); var yAxis = d3.svg.axis() .orient('left') .scale(y); var yAxisElem = svg.append('g') .attr('class', 'y axis') .attr('transform', translate(margin.left - 5, margin.top)); yAxisElem.call(yAxis); addIntersections(a, b) var linefn = d3.svg.line() .x(function(d) { return x(d.x); }) .y(function(d) { return y(d.y); }); var upperAreaFn = d3.svg.area() .x(function(d) { return x(d.a.x) }) .y0(function(d) { return y(d.b.y) }) .y1(function(d) { return y(Math.max(d.a.y, d.b.y)) }); var lowerAreaFn = d3.svg.area() .x(function(d) { return x(d.a.x) }) .y0(function(d) { return y(d.b.y) }) .y1(function(d) { return y(Math.min(d.a.y, d.b.y)) }); var zipped = zip({ a: a, b: b }); data = [ zipped ]; var lines = svg.append('g') .attr('transform', translate(margin.left, margin.top)) .selectAll('g.group') .data(data); var enterLines = lines.enter() .append('g') .attr('class', 'group'); // add them in backwards on purpose enterLines.append('path').attr('class', 'lower area') enterLines.append('path').attr('class', 'upper area') enterLines.append('path').attr('class', 'b line'); enterLines.append('path').attr('class', 'a line'); lines.selectAll('path.a.line') .attr('d', function(d) { return linefn(d.map(function(d) { return d.a; })) }); lines.selectAll('path.b.line') .attr('d', function(d) { return linefn(d.map(function(d) { return d.b; })) }); lines.selectAll('path.upper.area') .attr('d', upperAreaFn) lines.selectAll('path.lower.area') .attr('d', lowerAreaFn); // Inspired by Lee Byron's test data generator. // Borrowed from https://bl.ocks.org/mbostock/3943967 function bumpLayer(n, o) { function bump(a) { var x = 1 / (.1 + Math.random()), y = 2 * Math.random() - .5, z = 10 / (.1 + Math.random()); for (var i = 0; i < n; i++) { var w = (i / n - y) * z; a[i] += x * Math.exp(-w * w); } } var a = [], i; for (i = 0; i < n; ++i) a[i] = o + o * Math.random(); for (i = 0; i < 5; ++i) bump(a); return a.map(function(d, i) { return {x: i, y: Math.max(0, d)}; }); } function translate(x, y) { return "translate(" + x + ", " + y + ")"; } function zip(o) { var keys = d3.keys(o); var lengths = d3.values(o).map(function(d) { return d.length }); var length = lengths[0]; lengths.forEach(function(d) { if (d != length) { throw new TypeError("lengths of values to zip() must be equal") } }); var results = []; for (var i = 0; i < length; i++) { var result = {}; keys.forEach(function(k) { result[k] = o[k][i]; }); results.push(result); } return results; } function addIntersections(a, b) { if (a.length != b.length) { throw new TypeError("a.length != b.length"); } for (var i = 0; i < a.length - 1; i++) { var a1 = a[i], b1 = b[i], a2 = a[i + 1], b2 = b[i + 1]; if ((a1.y > b1.y && a2.y < b2.y) ^ (a1.y < b1.y && a2.y > b2.y)) { // calculate line segment intersection var s = (((b2.x - b1.x) * (a1.y - b1.y)) - ((b2.y - b1.y) * (a1.x - b1.x))) / (((b2.y - b1.y) * (a2.x - a1.x)) - ((b2.x - b1.x) * (a2.y - a1.y))); var t = { x: a1.x + (s * (a2.x - a1.x)), y: a1.y + (s * (a2.y - a1.y)) } var spliceIndex = ++i; a.splice(spliceIndex, 0, t); b.splice(spliceIndex, 0, t); } } } </script> </body> </html>
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js