Open 9am opened 1 year ago
I love SVG, especially the fascinating filter effects. But it's hard to learn and make some of your own effects. Usually, you learn by looking into the code made by others with the documentation. But there are 25 filter effect SVG elements, and each of them has its own attributes, not to mention the combination and composition of them.
So I decide to make a small tool to help me understand the structure of the filter and how each of them works.
(If you don't know what SVG filters can do, check out the further reading section[^1][^2])
First, we need to find a proper data structure to represent the filter and the connections between them.
A filter is composed of one or several SVG filter effect elements, the atomic part, which is kinda like a function in computer language. It takes inputs from the output of other effect elements, modifies the graphic, and passes the output to the next one. The output of the last effect element will be the result of this filter.
It's like the pedal board in front of a guitar player. The input signal is from the guitar, then each pedal modifies the signal to the next one. They can be arranged in different ways. Then the last pedal sends the output signal to the AMP.
So is it a tree? Wait, there are some elements doesn't work like a pure function. They can produce output by themselves, like feImage
. So if it's a tree, they'll be the leaf nodes. The problem is that their output can be used by more than one element. So not a tree. it's a graph, to be more specific, a Directed acyclic graph.
Let's group the filter elements by the way they take inputs:
Group | Thumbnail | Tag | Description |
---|---|---|---|
source | ββ | SourceGraphic``SourceAlpha BackgroundImage``BackgroundAlpha FillPaint``StrokePaint |
Not filter element. But can be used as input for other elements. |
noInput | ββ | feImage``feImage``feTurbulence |
Take no inputs. |
withInput | δΈββ | feColorMatrix``feComponentTransfer feConvolveMatrix``feDiffuseLighting feSpecularLighting``feDropShadow feGaussianBlur``feMergeNode feMorphology``feOffset``feTile |
Take 1 inputs. |
withInput2 | δΊββ | feBlend``feComposite``feDisplacementMap |
Take 2 inputs. |
withInputs | δΈββ | feMerge |
Take 1 or more inputs. |
We need to implement a parseFilter
function:
interface parseFilter {
(filter: SVGFilterElement): Graph;
}
interface Graph {
nodes: { id: string };
links: { source: string, target: string };
}
Based on what we know about the Filter Effect Group, we can loop through the children of \<filter> to find inputs for each item. Here is the implementation:
const parseFilter = (filter) => {
const [nodes, links] = [...filter.children].reduce(
([nodeMemo, linkMemo], child, i) => {
child.id = `${child.tagName}${ID_JOIN}${i}`;
const linkSet = getLinks(child, filter)
.filter((link) => link)
.reduce((memo, link) => new Set([...memo, link]), new Set());
return [
[...nodeMemo, child.id],
new Set([...linkMemo, ...linkSet])
];
},
[[...FE.SOURCE].map(([feName]) => feName), new Set()]
);
// [[source, target]]
const linksTuple = [...links].map((link) => link.split(LINK_JOIN));
const nodesInFilter = new Set(linksTuple.flatMap((item) => item));
return {
nodes: nodes
.filter((id) => nodesInFilter.has(id))
.map((id) => ({ id })),
links: linksTuple.map(([source, target]) => ({ source, target }))
};
};
The Sankey graph is a perfect way to demonstrate this data structure. I choose d3-sankey because it doesn't render the graph directly, just offers the data to render. So I can take control of the details of the graph. After giving different colors to each type of filter element and adjusting the connection line between them, this is what I got:
After adding some other features, seeing-svg-filter is born. It can:
Now I have the perfect tool to learn more about SVG filters. Maybe I'll try to implement some of them to understand better. And maybe move this tool further, to make another tool to easily generate a new filter effect. Hope you enjoy it, I'll see you next time.
@9am π
[^1]: Sophisticated Effects created with SVG Filters
[^2]: SVG Filter Effects: Outline Text with