feat(char): add rotate_from, scale and baseline jitter, and reflow - #450
Merged
Merged
Conversation
LeadcodeDev
force-pushed
the
ws-d-char-presets
branch
from
September 29, 2026 14:24
c16039b to
b2d27df
Compare
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
force-pushed
the
ws-d-char-presets
branch
from
September 29, 2026 14:28
b2d27df to
1b58c82
Compare
This was referenced Sep 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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_fromandink_fromalready set.rotate_fromtilts each unit and straightens it to zero by the end of thatunit's own animation.
rotate_originpicks the pivot:center, oredgefor theleading-edge hinge that reads as a staircase. It is excluded from
char_rotate_infor the same reason
scale_fromis excluded fromchar_scale_in— that presetalready owns the axis.
scale_jitterandbaseline_jittergive each unit a deterministic size andbaseline 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 fromthe same hash under different salts so they do not move in lockstep for one seed.
reflowchanges what a unit contributes to its line's width rather than whatit 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 centredwhile it is written, instead of reserving its final width from the first frame.
The typewriter complaint does not reproduce
The issue says a centred
typewritergrows from a fixed left edge. Probed againstthe real pipeline, it does not:
text.rstakesalign_widthfrom taffy's ownlayout.width, which is stable, so centred growth is already symmetric. Theasymmetry exists only in a non-finite-width fallback branch that taffy never
produces.
reflowis therefore a new capability here rather than a repair, andthe
char_fade_in+reflowpattern is the documented way to get it.reflowis accepted and inert onrich_text. The field lives on the sharedCharAnimationTiming, so the schema takes it, butrich_text's own char-animationrenderer 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.
reflowontypewriteris not implemented.AnimationEffect::Typewritercarries a bare
AnimationTiming, read by an exhaustive match invalidate_schema.rsthat the workstream could not touch. Puttingreflowon theshared
AnimationTiminginstead would make it silently inert on every otherpreset, 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_also_reaches_gradient_text_through_the_shared_render_pathproves thethree new knobs reach
gradient_textwith no change to that file, because both gothrough the shared
intrinsic::apply_text_anim_preset.cargo test -p rustmotion-core --lib715 passed ·-p rustmotion-components --lib333 ·--testsall green.