Prototype utility for generating continuous legends. Inspired by d3-legend.
Needs a pattern for configuring height, width, tick format, etc.
| border: no | |
| height: 260 | |
| license: gpl-3.0 |
| <!doctype html> | |
| <meta charset="utf-8"> | |
| <style> | |
| body { | |
| margin: 30px; | |
| } | |
| </style> | |
| <body> | |
| <div id="legend1" style="display: inline-block"></div> | |
| <div id="legend2" style="display: inline-block"></div> | |
| <div id="legend3" style="display: inline-block"></div> | |
| <div id="legend4" style="display: inline-block"></div> | |
| <div id="legend5" style="display: inline-block"></div> | |
| <script src="https://d3js.org/d3.v4.min.js"></script> | |
| <script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script> | |
| <script> | |
| var colorScale1 = d3.scaleSequential(d3.interpolatePlasma) | |
| .domain([0, 20]); | |
| var colorScale2 = d3.scaleSequential(d3.interpolateViridis) | |
| .domain([0, 1000]); | |
| var colorScale3 = d3.scaleSequential(d3.interpolateRainbow) | |
| .domain([0, 90]); | |
| var colorScale4 = d3.scaleSequential(d3.interpolateGnBu) | |
| .domain([0, 0.2]); | |
| var colorScale5 = d3.scaleSequential(d3.interpolatePRGn) | |
| .domain([-5, 5]); | |
| continuous("#legend1", colorScale1); | |
| continuous("#legend2", colorScale2); | |
| continuous("#legend3", colorScale3); | |
| continuous("#legend4", colorScale4); | |
| continuous("#legend5", colorScale5); | |
| // create continuous color legend | |
| function continuous(selector_id, colorscale) { | |
| var legendheight = 200, | |
| legendwidth = 80, | |
| margin = {top: 10, right: 60, bottom: 10, left: 2}; | |
| var canvas = d3.select(selector_id) | |
| .style("height", legendheight + "px") | |
| .style("width", legendwidth + "px") | |
| .style("position", "relative") | |
| .append("canvas") | |
| .attr("height", legendheight - margin.top - margin.bottom) | |
| .attr("width", 1) | |
| .style("height", (legendheight - margin.top - margin.bottom) + "px") | |
| .style("width", (legendwidth - margin.left - margin.right) + "px") | |
| .style("border", "1px solid #000") | |
| .style("position", "absolute") | |
| .style("top", (margin.top) + "px") | |
| .style("left", (margin.left) + "px") | |
| .node(); | |
| var ctx = canvas.getContext("2d"); | |
| var legendscale = d3.scaleLinear() | |
| .range([1, legendheight - margin.top - margin.bottom]) | |
| .domain(colorscale.domain()); | |
| // image data hackery based on http://bl.ocks.org/mbostock/048d21cf747371b11884f75ad896e5a5 | |
| var image = ctx.createImageData(1, legendheight); | |
| d3.range(legendheight).forEach(function(i) { | |
| var c = d3.rgb(colorscale(legendscale.invert(i))); | |
| image.data[4*i] = c.r; | |
| image.data[4*i + 1] = c.g; | |
| image.data[4*i + 2] = c.b; | |
| image.data[4*i + 3] = 255; | |
| }); | |
| ctx.putImageData(image, 0, 0); | |
| // A simpler way to do the above, but possibly slower. keep in mind the legend width is stretched because the width attr of the canvas is 1 | |
| // See http://stackoverflow.com/questions/4899799/whats-the-best-way-to-set-a-single-pixel-in-an-html5-canvas | |
| /* | |
| d3.range(legendheight).forEach(function(i) { | |
| ctx.fillStyle = colorscale(legendscale.invert(i)); | |
| ctx.fillRect(0,i,1,1); | |
| }); | |
| */ | |
| var legendaxis = d3.axisRight() | |
| .scale(legendscale) | |
| .tickSize(6) | |
| .ticks(8); | |
| var svg = d3.select(selector_id) | |
| .append("svg") | |
| .attr("height", (legendheight) + "px") | |
| .attr("width", (legendwidth) + "px") | |
| .style("position", "absolute") | |
| .style("left", "0px") | |
| .style("top", "0px") | |
| svg | |
| .append("g") | |
| .attr("class", "axis") | |
| .attr("transform", "translate(" + (legendwidth - margin.left - margin.right + 3) + "," + (margin.top) + ")") | |
| .call(legendaxis); | |
| }; | |
| </script> |