Skip to content

fix(ui): match the iOS number roll in AnimatedNumberText - #1662

Merged
bmc08gt merged 1 commit into
code/cashfrom
fix/number-roll-stagger
Oct 2, 2026
Merged

bmc08gt merged 1 commit into
code/cashfrom
fix/number-roll-stagger

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Oct 2, 2026

Copy link
Copy Markdown
Collaborator

The Android number roll felt faster and more abrupt than iOS. Frame-by-frame comparison against a 60fps recording of the iOS market cap readout (.contentTransition(.numericText()) with .snappy) showed the difference was mostly in how the digits move together, not the spring speed.

Before After
Order every slot starts on the same frame left to right, 12ms per position, like iOS
Mid-roll old glyph gone before new one appears, so the slot goes blank fades overlap, leaving two dim smudges
Quick scrub a digit changing every frame restarts from rest and looks held one spring loop per slot; a change retargets and keeps velocity
Settle no bounce damping 0.85, from .snappy's 0.15 bounce
New leading digit draws over the $ while its slot widens clipped to the slot's shown width
Blur cut square at each glyph's edges, visible on the last digit glyph layers padded by the blur radius

Notes:

  • Stiffness stays at 380. It matched iOS's per-digit timing in the recording; a softer spring matching SwiftUI's .default made each digit slower than iOS.
  • The stagger applies only to a slot at rest. A slot already rolling retargets immediately, otherwise a scrub would re-delay the right-hand digits every frame.
  • The blur fix is needed because Android renders a RenderEffect into a layer the size of its node. Padding the glyph layer by MaxBlur gives the blur room without changing the cell's measured size.
  • This changes every AnimatedNumberText: token cards, balance rows, market cap, the explainer, link cards and the amount area.

New tests: digits start left to right and a digit changing every frame still rolls out, both failing before the change.

Compared against a 60fps recording of the iOS market cap readout, which
uses `.contentTransition(.numericText())` with `.snappy`:

- Stagger: iOS starts each character about a frame after the one to its
  left. Android started every slot on the same frame, which read as
  abrupt. Each slot now waits 12ms per position, but only from rest; a
  slot already rolling retargets immediately so a scrub can't keep
  re-delaying it.
- Quick scrubs held a digit: every value change restarted the slot's
  animation from rest, so a value changing each frame barely moved.
  Each slot now runs one spring loop and a change only updates its
  target, keeping velocity.
- Mid-roll gaps: the outgoing glyph faded out before the incoming one
  faded in, so the slot went blank. The fades now overlap (out by 0.7,
  in over 0.2..0.9), leaving two dim smudges like iOS.
- Settle: damping 0.85, from `.snappy`'s 0.15 bounce. Stiffness stays
  at 380, which matched iOS's per-digit timing in the recording.
- New leading digits drew over the "$": the resize clip sat inside the
  slot's width layout, so it clipped to the glyph's full width. It now
  sits outside.
- Blur cut off square: Android renders a RenderEffect into a layer the
  size of its node, so each glyph's blur stopped at its own edges,
  visible on the last digit. Glyph layers are padded by the blur radius
  without changing the cell size.
@bmc08gt bmc08gt self-assigned this Oct 2, 2026
@github-actions github-actions Bot added type: fix Bug fix area: ui Compose UI, theme, components, resources and removed type: fix Bug fix labels Oct 2, 2026
@bmc08gt
bmc08gt merged commit d36f66b into code/cash Oct 2, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: ui Compose UI, theme, components, resources

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant