diff --git a/docs/features/marks.md b/docs/features/marks.md index 65ce03d53a..df4a9dff0c 100644 --- a/docs/features/marks.md +++ b/docs/features/marks.md @@ -476,6 +476,7 @@ All marks support the following style options: * **strokeDashoffset** - the [stroke dash offset](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dashoffset) (typically in pixels) * **opacity** - object opacity (a number between 0 and 1) * **mixBlendMode** - the [blend mode](https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode) (*e.g.*, *multiply*) +* **imageFilter** - a CSS [filter](https://developer.mozilla.org/en-US/docs/Web/CSS/filter) (*e.g.*, *blur(5px)*) * **shapeRendering** - the [shape-rendering mode](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/shape-rendering) (*e.g.*, *crispEdges*) * **paintOrder** - the [paint order](https://developer.mozilla.org/en-US/docs/Web/CSS/paint-order) (*e.g.*, *stroke*) * **dx** - horizontal offset (in pixels; defaults to 0) diff --git a/src/mark.d.ts b/src/mark.d.ts index 2989400944..64f4f496c9 100644 --- a/src/mark.d.ts +++ b/src/mark.d.ts @@ -399,6 +399,14 @@ export interface MarkOptions { */ mixBlendMode?: string; + /** + * A CSS [filter][1]; a constant string used to adjust the rendering of + * images, such as *blur(5px)*. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/CSS/filter + */ + imageFilter?: string; + /** * The [paint-order][1]; a constant string specifying the order in which the * **fill**, **stroke**, and any markers are drawn; defaults to *normal*, diff --git a/src/style.js b/src/style.js index 9c7fe40857..5c229e192a 100644 --- a/src/style.js +++ b/src/style.js @@ -44,6 +44,7 @@ export function styles( strokeDashoffset, opacity, mixBlendMode, + imageFilter, paintOrder, pointerEvents, shapeRendering @@ -135,6 +136,7 @@ export function styles( mark.ariaHidden = string(ariaHidden); mark.opacity = impliedNumber(copacity, 1); mark.mixBlendMode = impliedString(mixBlendMode, "normal"); + mark.imageFilter = impliedString(imageFilter, "none"); mark.paintOrder = impliedString(paintOrder, "normal"); mark.pointerEvents = impliedString(pointerEvents, "auto"); mark.shapeRendering = impliedString(shapeRendering, "auto"); @@ -368,6 +370,7 @@ export function applyIndirectStyles(selection, mark, dimensions, context) { applyAttr(selection, "stroke-dasharray", mark.strokeDasharray); applyAttr(selection, "stroke-dashoffset", mark.strokeDashoffset); applyAttr(selection, "shape-rendering", mark.shapeRendering); + applyAttr(selection, "filter", mark.imageFilter); applyAttr(selection, "paint-order", mark.paintOrder); applyAttr(selection, "pointer-events", mark.pointerEvents); } diff --git a/test/output/penguinSpeciesImageFilter.svg b/test/output/penguinSpeciesImageFilter.svg new file mode 100644 index 0000000000..24f4f65058 --- /dev/null +++ b/test/output/penguinSpeciesImageFilter.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + 0 + 20 + 40 + 60 + 80 + 100 + 120 + 140 + + + ↑ Frequency + + + + + + + + Adelie + Chinstrap + Gentoo + + + species + + + + + + + + + + \ No newline at end of file diff --git a/test/plots/index.ts b/test/plots/index.ts index fed29255e1..ee9e6ab576 100644 --- a/test/plots/index.ts +++ b/test/plots/index.ts @@ -195,9 +195,7 @@ export * from "./penguin-quantile-unknown.js"; export * from "./penguin-sex-mass-culmen-species.js"; export * from "./penguin-sex.js"; export * from "./penguin-size-symbols.js"; -export * from "./penguin-species-cheysson.js"; -export * from "./penguin-species-gradient.js"; -export * from "./penguin-species-group.js"; +export * from "./penguin-species.js"; export * from "./penguin-species-island-relative.js"; export * from "./penguin-species-island-sex.js"; export * from "./penguin-species-island.js"; diff --git a/test/plots/penguin-species-gradient.ts b/test/plots/penguin-species-gradient.ts deleted file mode 100644 index 8724060cdb..0000000000 --- a/test/plots/penguin-species-gradient.ts +++ /dev/null @@ -1,20 +0,0 @@ -import * as Plot from "@observablehq/plot"; -import * as d3 from "d3"; -import {svg} from "htl"; - -export async function penguinSpeciesGradient() { - const penguins = await d3.csv("data/penguins.csv", d3.autoType); - return Plot.plot({ - marks: [ - () => svg` - - - - - - `, - Plot.barY(penguins, Plot.groupX({y: "count"}, {x: "species", fill: "url(#gradient)"})), - Plot.ruleY([0]) - ] - }); -} diff --git a/test/plots/penguin-species-group.ts b/test/plots/penguin-species-group.ts deleted file mode 100644 index 94e03b58b9..0000000000 --- a/test/plots/penguin-species-group.ts +++ /dev/null @@ -1,13 +0,0 @@ -import * as Plot from "@observablehq/plot"; -import * as d3 from "d3"; - -export async function penguinSpeciesGroup() { - const penguins = await d3.csv("data/penguins.csv", d3.autoType); - return Plot.plot({ - marks: [ - Plot.barX(penguins, Plot.stackX(Plot.groupZ({x: "proportion"}, {fill: "species"}))), - Plot.text(penguins, Plot.stackX(Plot.groupZ({x: "proportion", text: "first"}, {z: "species", text: "species"}))), - Plot.ruleX([0, 1]) - ] - }); -} diff --git a/test/plots/penguin-species-cheysson.ts b/test/plots/penguin-species.ts similarity index 83% rename from test/plots/penguin-species-cheysson.ts rename to test/plots/penguin-species.ts index 43aa0040db..c7eacdde31 100644 --- a/test/plots/penguin-species-cheysson.ts +++ b/test/plots/penguin-species.ts @@ -28,3 +28,51 @@ export async function penguinSpeciesCheysson() { ] }); } + +export async function penguinSpeciesGradient() { + const penguins = await d3.csv("data/penguins.csv", d3.autoType); + return Plot.plot({ + marks: [ + () => svg` + + + + + + `, + Plot.barY(penguins, Plot.groupX({y: "count"}, {x: "species", fill: "url(#gradient)"})), + Plot.ruleY([0]) + ] + }); +} + +export async function penguinSpeciesGroup() { + const penguins = await d3.csv("data/penguins.csv", d3.autoType); + return Plot.plot({ + marks: [ + Plot.barX(penguins, Plot.stackX(Plot.groupZ({x: "proportion"}, {fill: "species"}))), + Plot.text(penguins, Plot.stackX(Plot.groupZ({x: "proportion", text: "first"}, {z: "species", text: "species"}))), + Plot.ruleX([0, 1]) + ] + }); +} + +export async function penguinSpeciesImageFilter() { + const penguins = await d3.csv("data/penguins.csv", d3.autoType); + return Plot.plot({ + marks: [ + Plot.barY( + penguins, + Plot.groupX( + {y: "count"}, + { + x: "species", + fill: "species", + imageFilter: "drop-shadow(3px 3px red) sepia(40%) drop-shadow(-3px -3px currentColor)" + } + ) + ), + Plot.ruleY([0]) + ] + }); +}