组件/TextMorph 文本形变

TextMorph 文本形变

把字符串切成有身份的段再 diff,只让真正变化的部分动起来;数字按位值滚动,容器宽高与字符同曲线过渡,支持弹簧缓动与打断续跑。

Verified自 2.4.14

TextMorph 文本形变

TxTextTransformer 的整串淡化不同:这里的动效单位是字符段,不是整个字符串。存活的段做 FLIP 位移,新增的段从最近的锚点淡入,离场的段脱离文档流后淡出,数字额外按位值纵向滚动。

基础用法

TextMorph

示例加载中...

数字位值滚动

数字默认按位值匹配,而不是从左到右:1,204 → 1,318 只滚百位和十位,千位原地不动;千分位逗号跟着量级走,999,999 → 1,000,000 时会滑过一整组。

量级跳跃达到 3 位及以上时不再携带任何数字——那时候数字已经糊成一片,整体替换才是对的。

numbers 关掉可退回字符级形变。

Number place value

示例加载中...

弹簧缓动

spring 接预设名或物理系数,来自 TuffEx 内部与 TxLiquidTxSlider 同一套弹簧编译器,所以整库的动效语言是一致的。弹簧同时决定曲线与时长,因此设了它之后 durationMseasing 都会被忽略。

Spring presets

示例加载中...

API

TxTextMorph Props

属性名类型默认值说明
textstring | number-要渲染的值。数字会先按 locale + decimals 格式化。
tagstringspan根节点 HTML 标签。
durationMsnumber400形变时长(ms)。设了 spring 时被忽略。
easingstringcubic-bezier(0.19, 1, 0.22, 1)CSS 缓动函数。设了 spring 时被忽略。
spring'snappy' | 'smooth' | 'bouncy' | { stiffness?, damping?, mass? }-弹簧物理。同时提供曲线与时长。
scalebooleantrue离场段是否带缩放。
numbersbooleantrue数字按位值滚动;关闭后退回字符级形变。
decimalsnumber-小数位数。仅当 text 是数字时生效。
localestringen用于分段与数字格式化。
cursorIndexnumber-光标位置。输入框场景下把单个数字从位值匹配切成光标匹配。
disabledbooleanfalse跳过动画,直接写入值。
respectReducedMotionbooleantrueprefers-reduced-motion: reduce 当作 disabled
debugbooleanfalse给根节点和每个段描边,用于排查形变异常。

TxTextMorph Events

事件名参数说明
animation-start-一次形变开始时触发;首帧渲染不触发。
animation-complete-形变自然走完。
animation-cancel-形变被下一次更新打断。

每次形变里 animation-completeanimation-cancel 恰好触发其一。

交互契约

  • 完整值以一个视觉隐藏但可读的 [tx-morph-sr] 节点存在;所有段元素都是 aria-hidden,屏幕阅读器只会读到完整值一次。
  • 段元素由引擎命令式创建,因此组件样式不是 scoped 的,作用域由 tx-morph-* 属性名保证。
  • 挂载后 Vue 不再渲染子节点——引擎接管了它们。首帧的纯文本只为 SSR 水合对齐。
  • prefers-reduced-motion: reducedisabled 下直接写入 textContent,并清空内部段记录,避免恢复动效后拿已移除的元素做 FLIP。
  • 形变中途再次更新会读取当前动画的速度并携带进新曲线,所以高频更新不会把每条曲线都停在起步阶段。
  • 根节点是 white-space: nowrap,换行由值里的 \n 生成 <br>;它不做软换行,也不做省略号截断。
  • cursorIndex 只在整个值里恰好有一个数字时生效。

最佳实践

  • 计数器、金额、进度百分比这类"同一个量在变"的值优先用它;numbers 的位值匹配就是为这类值写的。
  • 输入框里跟随用户键入的数字要传 cursorIndex,否则在 20 前面插一位会被理解成整列重新编号。
  • 需要软换行或省略号截断的文本用 TxTextTransformermode="fade",引擎表达不了这两件事。
  • 同一屏里不要放太多高频实例:每个字符都是一个带 will-change 的元素。
  • 想让容器跟着变化就直接用它——引擎自己动画宽高,不需要再套 TxAutoSizer

Source

  • Component source: packages/tuffex/packages/components/src/text-morph/src/TxTextMorph.vue
  • Engine: packages/tuffex/packages/components/src/text-morph/src/engine/,移植自 lochie/torph(MIT),弹簧层换成了 TuffEx 自己的 liquid/src/spring.ts
  • Types: packages/tuffex/packages/components/src/text-morph/src/types.ts 导出 TextMorphProps
  • Export alias: packages/tuffex/packages/components/src/text-morph/index.ts 导出 TextMorphTxTextMorphTextMorphPropsTxTextMorphInstance,以及引擎的 TextMorphEngine / MorphController
  • Coverage: packages/tuffex/packages/components/src/text-morph/__tests__/ 下的 engine.test.tstext-morph.test.ts 覆盖分段、diff 配对、位值匹配、弹簧融合与 reduced-motion 降级。
查看源码
packages/tuffex/packages/components/src/text-morph/index.ts