Skip to content

feat(animation): interpolate more properties, and name the ones that cannot - #173

Merged
LeadcodeDev merged 1 commit into
mainfrom
feat/generic-interpolation
Aug 11, 2026
Merged

feat(animation): interpolate more properties, and name the ones that cannot#173
LeadcodeDev merged 1 commit into
mainfrom
feat/generic-interpolation

Conversation

@LeadcodeDev

Copy link
Copy Markdown
Owner

Closes the "generic property interpolation" gap (High/M) from the re-scored Remotion differential — the ceiling on all of timeline and style.transition.

The silence was the bug

style.transition promised smoothing that exactly two properties delivered: opacity, and color on text/counter. Everything else snapped at the step's at, and the field's own doc comment admitted it. But validate never looked at style.transition, nor at the diffs between timeline states — so a transition on width or border-radius passed clean and rendered as a jump.

Measured before any fix, sampling a 1s transition at its midpoint:

border_radius_interpolates_at_midpoint: expected a value strictly between 0 and 40, got 40
background_color_interpolates_at_midpoint: got #ffffff at the midpoint

Diagnosing comes first

check_transition_smoothing replays the timeline in the author's order, diffs each field against the running state, and classifies every touched property into four buckets — each with its own message rather than one generic complaint:

Warning: … style.transition is set and timeline changes `width`, but `width` affects layout
(box size/position/flow) — rustmotion cannot interpolate a layout property without re-running
layout on every sampled frame, so it will snap instead of animating. Consider approximating the
motion with `transform: translate`/`scale` instead, which is paint-time and does interpolate.

Warning: … style.transition is set and timeline changes `display`, but `display` is a discrete
CSS property (no value exists in between the two states) — it always snaps at the step's `at`,
exactly like real CSS. This is expected, not a rustmotion limitation.

The second message matters as much as the first: it stops an author chasing a bug that is not one. The check only runs when style.transition is actually set — nothing was promised otherwise, so nothing is warned about.

Then interpolation, for what can be done honestly

background (solid colours) and border-radius (uniform, absolute px) now animate. They are resolved onto CssStyle before layout, where paint_pass already reads them for the static case — so neither frozen pass needed changing, and the interpolation maths is the existing keyframe solver rather than a second copy of it.

Layout properties are deliberately left snapping. Interpolating width without re-running layout would put the measured box and the painted pixels out of step and blind the geometry validator — the class of bug this repository already spent a chantier repairing. Signalled, not simulated.

Mixed units are refused, not guessed. box_builder runs before layout, so % and em have no trustworthy base yet. The diagnostic and the runtime share one predicate for what counts as resolvable, so they cannot drift into disagreeing.

Scope, stated plainly

Interpolable now: opacity, color, background (solid), border-radius (uniform px).

Diagnosed, not implemented: transform (pairing two function lists of different length or order is ambiguous — guessing would be worse than saying so), box-shadow / filter / clip-path (continuous but multi-field), per-corner radius, relative units, and every layout property.

Removed rather than shipped

I had written a second diagnostic for unknown Animation.property values, then found on testing that schema/video.rs already rejects them at parse time with a did-you-mean. The redundant code was deleted and a test pins the existing behaviour — dead code that looks like a feature is worse than no code.

Verification

  • cargo test --workspace: 26 targets, 1046 tests, 0 failures
  • cargo fmt --all --check and cargo clippy --workspace --all-targets -- -D warnings: clean
  • No shipped example changes render. None of the eight declares timeline on a component — verified by walking the JSON, not by grepping for the word (mega-showcase and component-showcase both contain "timeline" as a component type, which would have made a grep lie).

…cannot

`style.transition` promised smoothing that two properties delivered. Every
other property snapped at the step's `at`, and the field's own doc comment
admitted it — but nothing told the author. `validate` never looked at
`style.transition` or at the diffs between `timeline` states, so a
transition on `width` or `border-radius` passed clean and rendered as a
jump.

The missing capability was not the problem. The silence was.

**Diagnosing comes first.** `check_transition_smoothing` replays the
timeline in the author's order, diffs each field against the running state,
and classifies every touched property into four buckets, each with its own
message. A layout property explains *why* it cannot interpolate and points
at `transform: translate`/`scale` as the paint-time alternative; a discrete
property says the snap is expected CSS behaviour, not a rustmotion
limitation — so nobody chases a bug that is not one. The check only runs
when `style.transition` is actually set: nothing was promised otherwise.

**Then interpolation, for what can be done honestly.** `background` (solid
colours) and `border-radius` (uniform, absolute px) now animate. They are
resolved onto `CssStyle` before layout, where `paint_pass` already reads
them for the static case — so nothing in the frozen paint or layout passes
had to change, and the interpolation maths is the existing keyframe solver,
not a second one.

Layout properties are deliberately left snapping. Interpolating `width`
without re-running layout would put the measured box and the painted pixels
out of step and blind the geometry validator — the class of bug this
repository already spent a chantier repairing. Signalled, not simulated.

Mixed units are refused rather than guessed: `box_builder` runs before
layout, so `%` and `em` have no trustworthy base yet. The diagnostic and the
runtime share one predicate for what is resolvable, so they cannot drift
into disagreeing about it.

Also removed: a diagnostic I had written for unknown `Animation.property`
values before discovering that `schema/video.rs` already rejects them at
parse time with a did-you-mean. A test pins that behaviour instead, rather
than shipping dead code that looks like a feature.

No shipped example changes: none of the eight declares `timeline` on a
component, verified by walking the JSON rather than by grep.
@LeadcodeDev LeadcodeDev added the enhancement New feature or request label Aug 11, 2026
@LeadcodeDev LeadcodeDev self-assigned this Aug 11, 2026
@LeadcodeDev
LeadcodeDev merged commit 870b481 into main Aug 11, 2026
3 checks passed
@LeadcodeDev
LeadcodeDev deleted the feat/generic-interpolation branch August 11, 2026 10:41
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.

1 participant