Ways to morph
Soft, even and index, the three ways side by side, the four steps that make soft what it is, and a frame as anchors and handles.
morph(from, to, way) takes a way to morph, or { way, extra }. Soft is the default and the reason for the package; the other two are here to compare, and for the drawings they suit.
// A stroke is moved as a whole: where it is, how big it is and which way it
// faces are interpolated apart from its shape. A plus turns into an x; it
// doesn't shrink through the middle, as points moving in straight lines do.
const size = Math.exp(lerp(Math.log(fromSize), Math.log(toSize), t));
const centre = lerpVec(fromCentre, toCentre, t);
const points = fromShape.map((p, i) => {
const [x, y] = rotate(lerpVec(p, toShape[i], t), turn * t);
return [centre[0] + size * x, centre[1] + size * y];
});
// Then it's softened along its length: a Gaussian blur over its points that
// peaks halfway and is gone at either end. Corners appear rounded and tighten
// as they arrive, instead of snapping in as kinks.
const soft = soften(points, closed, SOFTEST * Math.sin(Math.PI * t));
// Drawn as a few cubics through anchors chosen once for the whole morph (every
// corner, then as few as keep each frame within 0.04 of its points), sharp only
// at an end's own corners, near that end.
const ease = (i) => 1 - Math.max(cornerFrom[i] ? (1 - t) ** 3 : 0, cornerTo[i] ? t ** 3 : 0);
const nodes = nodesThrough({ points: soft, ease }, closed, anchors);
path.setAttribute("d", nodesData(nodes, closed));
Soft
Pieces are paired by their shape, each moved whole (its place, size and turn apart from its shape), blurred along its length so a corner arrives rounded, and drawn as a few fitted cubic curves, sharp only at an end's own corners near that end. It's "soft", and what morph does unless told otherwise.
Even
"even" resamples each outline into points a unit apart, matches them for the least travel and moves them in straight lines, drawn as a polygon: flubber's way.
Index
"index" pairs path commands in the order they were written, as most morphing libraries do. It's right when both sides were drawn alike, the same commands in the same order; otherwise points cross over.
Steps
Soft is even with four steps on, each switchable alone, to see what each does:
import { EVENLY, morph } from "@cesarvenzac/morph";
morph(Plus, X, { ...EVENLY, shape: true }); // even, paired by shapeshape: pair points by the shape they make, not by how far they travel.whole: move each piece whole, its place, size and turn apart from its shape.round: blur each outline along its length, most halfway, so a corner arrives rounded.curves: draw fitted cubics rather than a polygon, sharp only at an end's own corners.
SOFTLY is all four on, soft itself, and EVENLY none.
Anchors and handles
shapes(t) is the frame as an editor shows a path: each piece's nodes, an anchor with its in and out handles, whether it's closed, its width, and an island's holes. Between 0 and 1, at is drawn from them, so the two agree; at 0 and 1 at is each side exactly, and shapes the way's own take on it. nodesData(nodes, closed) writes nodes back as path data. points(t) is each part's outline before it's drawn as curves, for measuring.
import { nodesData } from "@cesarvenzac/morph";
for (const shape of m.shapes(t)) {
outline(nodesData(shape.nodes, shape.closed)); // the path
for (const node of shape.nodes) dot(node.at); // its anchors
}Why each step is there, measured, is in Morphs without spikes.