TextMorph
Text that animates only the segments that changed.
Usage
Basic
Only the segments that actually changed move, and the container's size follows on the same curve.
Loading demo...
Number Place Value
Digits match by place value, not left to right: 1,204 → 1,318 rolls the hundreds and tens and leaves the thousands alone.
Loading demo...
Springs
spring takes a preset name or coefficients and sets both curve and duration, so durationMs and easing are ignored.
Loading demo...
Best Practices
- Reach for it where the same quantity changes: counters, totals, percentages.
- Pass
cursorIndexfor a number the user is typing, or inserting a digit before20reads as renumbering the column. - Use
TxTextTransformerwithmode="fade"where text must soft-wrap or ellipsise; here only\nbreaks a line. - Keep high-frequency instances on one screen few; every character is an element with
will-change. - Don't wrap it in
TxAutoSizer; the engine animates the container's width and height itself.
API Reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
text | string | number | - | The value to render; numbers are formatted with locale and decimals first. |
tag | string | span | Root element tag. |
durationMs | number | 400 | Morph duration in ms; ignored when spring is set. |
easing | string | cubic-bezier(0.19, 1, 0.22, 1) | CSS timing function; ignored when spring is set. |
spring | 'snappy' | 'smooth' | 'bouncy' | { stiffness?, damping?, mass? } | - | Spring physics; sets both the curve and the duration. |
scale | boolean | true | Scales exiting segments as they leave. |
numbers | boolean | true | Rolls numbers by place value; off falls back to the character morph. |
decimals | number | - | Fraction digits; applies only when text is a number. |
locale | string | en | Used for segmentation and number formatting. |
cursorIndex | number | - | Caret position; switches a single-number value to caret matching. |
disabled | boolean | false | Skips the animation and writes the value straight in. |
respectReducedMotion | boolean | true | Treats reduced motion as disabled. |
debug | boolean | false | Outlines the root and every segment, for debugging a morph. |
Events
| Event | Payload | Description |
|---|---|---|
animation-start | - | A morph began; never fires for the first render. |
animation-complete | - | The morph ran to its end; exactly one of this and animation-cancel fires per morph. |
animation-cancel | - | The next update interrupted the morph. |
Overview
- The unit of motion is the character segment, not the string: survivors FLIP to their new position, arrivals fade in from the nearest anchor, and exits leave the flow and fade out.
- The whole value lives in one visually hidden
[tx-morph-sr]node and every segment isaria-hidden, so a screen reader announces the value once. - Selectable segments keep the original whitespace (NBSP, repeated spaces, tabs, line breaks, combining characters); exiting segments are not selectable, so a copy never mixes in the previous text.
- Grouping commas travel with the magnitude; once the magnitude jumps by three places or more, no digit carries across and the number is replaced.
- An update mid-morph carries the current velocity into the new curve, so rapid updates don't stall at the start of each curve.
- Under reduced motion or
disabled, the value is written straight totextContentand the internal segment record is cleared.
Technologies
- The engine creates and owns the segment elements imperatively (the first-paint text exists only for SSR hydration), so the styles aren't scoped;
tx-morph-*attribute names contain them. - The engine (also exported as
TextMorphEngine/MorphController) is ported from lochie/torph (MIT), with its spring layer replaced byliquid/src/spring.ts, shared withTxLiquid. - Source:
packages/tuffex/packages/components/src/text-morph/.
查看源码
packages/tuffex/packages/components/src/text-morph/index.ts