Skip to content

Commit 6db63fc

Browse files
Filmbostock
andauthored
opacity legend (#587)
* opacity legend * add color option Co-authored-by: Mike Bostock <[email protected]>
1 parent 740605f commit 6db63fc

File tree

12 files changed

+293
-7
lines changed

12 files changed

+293
-7
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ Continuous color legends are rendered as a ramp, and can be configured with the
258258

259259
#### Plot.legend({[*name*]: *scale*, ...*options*})
260260

261-
Returns a legend for the given *scale* definition, passing the options described in the previous section. The only supported name for now is *color*.
261+
Returns a legend for the given *scale* definition, passing the options described in the previous section. Currently supports only *color* and *opacity* scales. An opacity scale is treated as a color scale with varying transparency.
262262

263263
### Position options
264264

src/legends.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import {normalizeScale} from "./scales.js";
22
import {legendColor} from "./legends/color.js";
3+
import {legendOpacity} from "./legends/opacity.js";
4+
import {isObject} from "./mark.js";
35

46
const legendRegistry = new Map([
5-
["color", legendColor]
7+
["color", legendColor],
8+
["opacity", legendOpacity]
69
]);
710

811
export function legend(options = {}) {
912
for (const [key, value] of legendRegistry) {
1013
const scale = options[key];
11-
if (scale != null) {
14+
if (isObject(scale)) { // e.g., ignore {color: "red"}
1215
return value(normalizeScale(key, scale), legendOptions(scale, options));
1316
}
1417
}

src/legends/opacity.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import {rgb} from "d3";
2+
import {legendColor} from "./color.js";
3+
4+
const black = rgb(0, 0, 0);
5+
6+
export function legendOpacity({type, interpolate, ...scale}, {
7+
legend = "ramp",
8+
color = black,
9+
...options
10+
}) {
11+
if (!interpolate) throw new Error(`${type} opacity scales are not supported`);
12+
if (`${legend}`.toLowerCase() !== "ramp") throw new Error(`${legend} opacity legends are not supported`);
13+
return legendColor({type, ...scale, interpolate: interpolateOpacity(color)}, {legend, ...options});
14+
}
15+
16+
function interpolateOpacity(color) {
17+
const {r, g, b} = rgb(color) || black; // treat invalid color as black
18+
return t => `rgba(${r},${g},${b},${t})`;
19+
}

src/mark.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,12 +173,15 @@ export function arrayify(data, type) {
173173
: (data instanceof type ? data : type.from(data)));
174174
}
175175

176+
// Disambiguates an options object (e.g., {y: "x2"}) from a primitive value.
177+
export function isObject(option) {
178+
return option && option.toString === objectToString;
179+
}
180+
176181
// Disambiguates an options object (e.g., {y: "x2"}) from a channel value
177182
// definition expressed as a channel transform (e.g., {transform: …}).
178183
export function isOptions(option) {
179-
return option
180-
&& option.toString === objectToString
181-
&& typeof option.transform !== "function";
184+
return isObject(option) && typeof option.transform !== "function";
182185
}
183186

184187
// For marks specified either as [0, x] or [x1, x2], such as areas and bars.

test/output/opacityLegend.svg

Lines changed: 37 additions & 0 deletions
Loading

test/output/opacityLegendColor.svg

Lines changed: 37 additions & 0 deletions
Loading
Lines changed: 37 additions & 0 deletions
Loading

test/output/opacityLegendLog.svg

Lines changed: 49 additions & 0 deletions
Loading

test/output/opacityLegendRange.svg

Lines changed: 37 additions & 0 deletions
Loading

test/output/opacityLegendSqrt.svg

Lines changed: 37 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)