Skip to content

feat(clip-path): a morph can pass through intermediate shapes - #405

Merged
LeadcodeDev merged 1 commit into
mainfrom
feat/clip-path-keyframes
Sep 28, 2026
Merged

LeadcodeDev merged 1 commit into
mainfrom
feat/clip-path-keyframes

Conversation

@LeadcodeDev

Copy link
Copy Markdown
Owner

Closes #384. Refs #388.

The decision, and why it is neither option I offered

#398 landed clip-path animation in a different spelling from the one this issue describes, and I left it open with the choice written into the issue: finish the literal property: "clip-path" form with shape-valued keyframes, or re-scope.

On reflection, neither. The spelling was never the gap — the limit to two shapes was.

A second vocabulary for one capability is worse than one good one, and a shape-valued keyframe would have to re-earn the easing, spring, loop and per-keyframe easing that the scalar clip_path_progress track already provides for free, precisely because it is an ordinary motion property.

So the chain gets longer instead:

"clip-path": {
  "kind": "morph",
  "from": { "kind": "circle", "radius": 20 },
  "via":  [ { "kind": "circle", "radius": 140 } ],
  "to":   { "kind": "circle", "radius": 60 }
}

How it behaves

progress spans the whole chain, not each leg: with one via, 0.5 lands exactly on it and 0.25 is halfway along the first leg. Legs are equal in progress whatever the geometric distance between the shapes — an uneven pace is the easing's job, and putting it in via would have made two knobs fight over the same thing.

Each consecutive pair follows the same rule as a two-shape morph: same kind, same vertex count for a polygon. An incompatible pair mid-chain warns on stderr and leaves the node unclipped, exactly as a lone incompatible pair does — no new failure mode.

Verification

Four tests. Three fail without the chain, confirmed by short-circuiting morph_leg:

a_morph_chain_passes_through   FAILED
a_morph_chain_keeps_moving     FAILED
a_via_shape_splits             FAILED
an_empty_via_leaves            ok        ← the compatibility pin, correctly

an_empty_via_leaves_the_two_shape_morph_exactly_as_it_was asserts on morph_leg directly rather than through pixels: with no via, the endpoints and the progress pass through untouched. That is the clearest possible statement that nothing written before this renders differently.

One test replaced an earlier version of itself that was simply wrong. It compared a chain at progress 1.0 against a direct morph at 0.5 — which land on different shapes by construction. It asserted something untrue, and failed for the right reason. Worth saying, because a test that fails for a bad reason is easy to "fix" by loosening it.

cargo fmt --all --check, cargo clippy --workspace --all-targets -- -D warnings, cargo test --workspace (1722) all clean.

Written comment-free, per the codebase-wide rule from #345.

#398 delivered clip-path animation in a different spelling from the one #384
asked for, and I left the issue open with the choice written down: finish the
literal `property: "clip-path"` form with shape-valued keyframes, or re-scope.

Neither, on reflection. The spelling was never the gap — the limit to two
shapes was. A second vocabulary for one capability is worse than one good one,
and a shape-valued keyframe would have to re-earn the easing, spring and loop
handling that the scalar track already gives for free.

So `Morph` gains `via`: shapes the sweep passes through, in order.

    "clip-path": {
      "kind": "morph",
      "from": { "kind": "circle", "radius": 20 },
      "via":  [ { "kind": "circle", "radius": 140 } ],
      "to":   { "kind": "circle", "radius": 60 }
    }

`progress` spans the whole chain rather than each leg, and the legs are equal in
progress whatever the geometric distance between shapes — an uneven pace is the
easing's job, not the chain's. Each consecutive pair follows the same
interpolation rule as a two-shape morph, and an incompatible pair mid-chain
warns and leaves the node unclipped exactly as a lone incompatible pair does.

`via` absent is byte-for-byte what a morph has always been, which is what a
unit test on `morph_leg` asserts directly rather than through pixels.

Four tests. Three fail without the chain. The fourth is the compatibility pin
and passes both ways on purpose.

One of them replaced an earlier version of itself that was simply wrong: it
compared a chain at progress 1.0 against a direct morph at 0.5, which land on
different shapes by construction. It asserted something untrue and failed for
the right reason.

Closes #384
@LeadcodeDev LeadcodeDev added the enhancement New feature or request label Sep 28, 2026
@LeadcodeDev LeadcodeDev self-assigned this Sep 28, 2026
@LeadcodeDev
LeadcodeDev merged commit bff1703 into main Sep 28, 2026
4 checks passed
@LeadcodeDev
LeadcodeDev deleted the feat/clip-path-keyframes branch September 29, 2026 18:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Animate clip-path: interpolate polygon, inset, circle and ellipse in keyframes

1 participant