Skip to content

feat(char): add rotate_from, scale and baseline jitter, and reflow - #450

Merged
LeadcodeDev merged 3 commits into
mainfrom
ws-d-char-presets
Sep 29, 2026
Merged

LeadcodeDev merged 3 commits into
mainfrom
ws-d-char-presets

Conversation

@LeadcodeDev

Copy link
Copy Markdown
Owner

Refs #429. Part of #438. Two gaps stated below — read them before closing the issue.

The three parts of #363 that were never delivered, all on CharAnimationTiming,
following the pattern scale_from and ink_from already set.

rotate_from tilts each unit and straightens it to zero by the end of that
unit's own animation. rotate_origin picks the pivot: center, or edge for the
leading-edge hinge that reads as a staircase. It is excluded from char_rotate_in
for the same reason scale_from is excluded from char_scale_in — that preset
already owns the axis.

scale_jitter and baseline_jitter give each unit a deterministic size and
baseline offset that decays to zero, so a line is born uneven and settles. They
are independent of jitter, which only perturbs timing, and the three draw from
the same hash under different salts so they do not move in lockstep for one seed.

reflow changes what a unit contributes to its line's width rather than what
it paints: a unit that has not started contributes nothing, and a starting unit's
contribution eases in under reflow_easing. A centred line therefore stays centred
while it is written, instead of reserving its final width from the first frame.

The typewriter complaint does not reproduce

The issue says a centred typewriter grows from a fixed left edge. Probed against
the real pipeline, it does not: text.rs takes align_width from taffy's own
layout.width, which is stable, so centred growth is already symmetric. The
asymmetry exists only in a non-finite-width fallback branch that taffy never
produces. reflow is therefore a new capability here rather than a repair, and
the char_fade_in + reflow pattern is the documented way to get it.

⚠️ Two gaps, named rather than shipped quietly

reflow is accepted and inert on rich_text. The field lives on the shared
CharAnimationTiming, so the schema takes it, but rich_text's own char-animation
renderer precomputes token positions and pill-background bounds independently and
was not touched. That violates this repository's rule that a field the validator
accepts must have an effect. It needs either the implementation or a validator
refusal before #429 closes — I am handling it separately and this PR does not
claim it.

reflow on typewriter is not implemented. AnimationEffect::Typewriter
carries a bare AnimationTiming, read by an exhaustive match in
validate_schema.rs that the workstream could not touch. Putting reflow on the
shared AnimationTiming instead would make it silently inert on every other
preset, which is the same violation. Given that the complaint does not reproduce,
leaving it out looks right — but it is a decision, not an omission.

Tests

Six render tests — scenario, layout, paint, read pixels — each reverted and
watched go red:

rotate_from must visibly tilt the unit mid-animation: plain width=12px, tilted width=12px
scale_jitter must visibly perturb a unit's size mid-animation
reflow must keep the growing centred line's ink centred on the box: box center=350, got ink center=204

rotate_from_also_reaches_gradient_text_through_the_shared_render_path proves the
three new knobs reach gradient_text with no change to that file, because both go
through the shared intrinsic::apply_text_anim_preset.

cargo test -p rustmotion-core --lib 715 passed · -p rustmotion-components --lib 333 · --tests all green.

@LeadcodeDev LeadcodeDev added the enhancement New feature or request label Sep 29, 2026
@LeadcodeDev LeadcodeDev self-assigned this Sep 29, 2026
The three parts of #363 that were never delivered, all on
CharAnimationTiming, following the pattern scale_from and ink_from
already set.

rotate_from tilts each unit and straightens it to zero by the end of
that unit's own animation. rotate_origin picks the pivot: center, or
edge for the leading-edge hinge that reads as a staircase. It is
excluded from char_rotate_in for the same reason scale_from is excluded
from char_scale_in -- that preset already owns the axis.

scale_jitter and baseline_jitter give each unit a deterministic size and
baseline offset that decays to zero, so a line is born uneven and
settles. They are independent of jitter, which only perturbs timing, and
the three draw from the same hash under different salts so they do not
move in lockstep for one seed.

reflow changes what a unit contributes to its line's width rather than
what it paints: a unit that has not started contributes nothing, and a
starting unit's contribution eases in under reflow_easing. A centred
line therefore stays centred while it is written, instead of reserving
its final width from the first frame.

The complaint that a typewriter grows from a fixed left edge does not
reproduce in the real pipeline. text.rs takes align_width from taffy's
own layout.width, which is stable, so centred growth is already
symmetric; the asymmetry exists only in a non-finite-width fallback
branch that taffy never produces.

Refs #429
from_deg is already f32, so the cast was a no-op; the width binding in
the fold was returned immediately after being named. Both are lints CI
runs with -D warnings and a scoped -p run does not surface.
reflow lives on the shared CharAnimationTiming, so the schema takes it
on a rich_text and nothing happens: that component's char animation lays
out from token positions computed once, together with the pill
backgrounds, so a unit that has not started still holds its slot.

A field the validator accepts must have an effect or say why it does
not. Implementing it there means re-deriving token offsets and pill
bounds from reflowed cumulative widths, which is a different piece of
work from the shared path; until someone does it, the warning names the
component and points at text and gradient_text.

char-animation-tuning.md documents the three new knobs and this gap.
@LeadcodeDev
LeadcodeDev merged commit d729e55 into main Sep 29, 2026
4 checks passed
@LeadcodeDev
LeadcodeDev deleted the ws-d-char-presets branch September 29, 2026 14:39
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