Building on my last article, Pretty Metaballs, I have seen a number of metaball examples that connect two circles, or that do so in a hub-and-spoke fashion.
However, I wanted to create a function that would generate a single SVG
path representing a meta-metaball! The function
f(g) -> path would take
g, a group element containing two or more circle elements, and output a closed
path (ends with the
Z command) around these circles. The result would look as follows:
Here’s an example of an algorithm to do just that, as well as issues that might arise when building this tool:
- Traverse the DOM and get all
circleelements with the group.
- Sort circles (i.e. vertically, horizontally, by Euclidean distance, etc).
- Find the 4 tangent points (TP) and 4 bezier curve handle points (see http://varun.ca/metaballs/)
- Start the path with
M(moveTo) at TP 1 between circles 1 & 2.
- Draw a bezier curve,
C, between TPs 1 & 2.
- Draw an arc,
A, between TP 2 (circles 1–2) and TP 1 (circles 2–3).
- For terminal circles (
length-1), draw an arc between TP 2 & 3.
- Repeat twice (iterate once sorted and once reversed).
- Close the path with
Z(the final arc should close where we started).
To illustrate this algorithm, I’ve created a graphic to show tangent points in red, handle points in yellow, and labels for the various components of the path (i.e. arcs & curves).
Common issues when writing SVG algorithms
childNodesdoesn’t work for SVG elements and
childrenfor SVG isn’t widely supported. Instead, I use
getElementsByTagNameshould work as well.
document.createElementdoesn’t work for SVG elements, you have to
document.createElementNSinstead (and supply the SVG XML namespace).
- Pay attention to the largeArcFlag & sweepFlag!
- If you want to use SVG line animations, parameterize the starting point so the animation starts in a place of your choosing.
Where can we take it from here?
We can apply this algorithm to two or more partially-overlapping metaballs and use the CSS property
mix-blend-mode to control how these paths blend together. In the above example, I’m using
That’s it for now, happy coding!