Skip to content

Shapes and colours

How pieces pair, how holes open and close, what becomes of the shapes one side has more of, and how colours mix.

@cesarvenzac/morphv1.0.0

Pieces

A drawing is pieces. Each subpath of a stroked part is a stroke, and each filled outline, with the holes directly inside it, is an island, found as SVG's fill rule finds them. Pieces are paired across the two sides by their shape (its size, place and turn set aside), by how far apart they are and by how their sizes differ, the pairing that costs least overall: a circle near a circle becomes it, rather than turning into a bar.

A stroke pairs with a fill only when nothing fits better, and one then crossfades into the other as it moves.

Holes

An island's holes are paired among themselves and move with it. A hole with no partner opens from a point, or closes into one, where it is: the counter of an “o” shrinks away inside the letter as it becomes an “l”, rather than flying off.

One side has more

extra says what becomes of the pieces one side has more of:

  • "auto", the default: small pieces lying along a much bigger one join it, as three dots become a line or a loader's rays a circle; the rest gather.
  • "split": every extra piece joins the nearest piece of its kind on the other side, so one shape splits into several, or several merge into one. A stroke is shared out between them, a stretch each; a filled shape is cut into parts, one each, about as big as each, so a heart breaks into the letters of a word. A stroke paired with a fill would only crossfade, so they part when either has a piece of its kind to join: letters becoming a sun merge into its disc while its rays grow.
  • "gather": each extra piece gathers into a dot as the morph goes on, or grows from one.

A filled shape with holes, or one that won't cut cleanly, isn't cut: each piece becomes the whole of it, the copies fading as they land. A piece with nothing of its kind on the other side gathers whatever extra says. And "index" pairs commands in order, so it takes no extra.

// The triangle divides into the two bars.
morph(play, pause, { extra: "split" });
auto
split
gather
Pair
The same pair three ways. Play to pause is where split shines, dots to a line where auto joins them.

Colours

Colours mix in OKLab, alpha premultiplied, as CSS's color-mix() mixes them, so a red becoming a blue passes through a clear purple, between the two in lightness, not the dark one an sRGB mix gives. Colours CSS names without a page, hex, rgb(), hsl() and the named colours, are mixed by Morph into hex. Anything else (the current colour, a CSS variable, oklch()) is mixed by the browser: the part's colour is then a color-mix(in oklab, …), which SVG's paint attributes take.

Opacity and stroke widths are interpolated, and caps and joins are the nearer side's.

Parts

A frame of drawings is parts of { d, width, fill, color?, opacity?, cap?, join? }: width the stroke's width in the canvas's units (0 for a fill), and fill how filled it is, from 0, a stroke, to 1, a fill, between while one becomes the other. color and opacity are left out for the current colour and full opacity. A frame of two icons is simpler, { d, width }, each a stroke in the current colour.

attributesOf turns a part into a <path>'s attributes, the same set every frame, so nothing is left over from the last. Fill with the even-odd rule, as it does: an island's holes are in its path data.

import { attributesOf } from "@cesarvenzac/morph";

attributesOf(m.at(0.5)[0]);
// { d: "M12 3.6C…", fill: "#f0a35c", stroke: "none",
//   "fill-opacity": "1", "fill-rule": "evenodd", … }