Vant RollingText 翻滚文本组件实战指南:数字与文本翻滚动效的配置、原理与手动控制
【免费下载链接】vantA lightweight, customizable Vue UI library for mobile web apps.项目地址: https://gitcode.com/GitHub_Trending/va/vant
RollingText(翻滚文本)是 Vant 移动端组件库提供的一款文本翻滚动效组件,可以像老虎机一样翻滚数字,也可以翻滚任意等长文本,常用于大屏数据看板、金额/人数等统计数字的播报场景。本文基于当前仓库中 Vant 4.10.0 的组件实现,完整讲解 RollingText 的全部属性、实例方法、样式定制方式,并结合源码剖析其位数补零、数字序列构建、CSS 关键帧动画与实例方法暴露等底层原理,帮助你在项目中快速落地可复用的翻滚动效。
组件简介与版本要求
RollingText 组件位于 packages/vant/src/rolling-text,由RollingText.tsx(外层组件)、RollingTextItem.tsx(单个数位子组件)、index.less(样式)、types.ts(类型定义)组成,并通过 index.ts 使用withInstall封装为可全局注册的插件。
使用该组件需要将vant升级到>= 4.6.0版本(当前仓库packages/vant/package.json中的版本为4.10.0)。它基于 Vue 3 编写,依赖vue: ^3.0.0。
组件支持两类翻滚内容:
- 数字:通过
start-num/target-num指定起始与目标数值,自动完成翻滚; - 非数字文本:通过
text-list传入等长字符串数组,从第一项翻滚到最后一项。
引入组件
在应用入口通过app.use()全局注册组件,即可在模板中使用<van-rolling-text>:
import { createApp } from 'vue'; import { RollingText } from 'vant'; const app = createApp(); app.use(RollingText);注册后组件会以van-rolling-text的标签名全局可用。除全局注册外,也支持按需引入与局部注册,更多注册方式可参考 组件注册。从 index.ts 的声明可以看到,组件还会向 Vue 全局组件类型中注入VanRollingText,因此在 TypeScript 项目中可以直接获得模板内的类型提示。
代码演示与属性详解
基础用法:从起始数值翻滚到目标数值
通过start-num设置起始数值,target-num设置目标数值。RollingText 默认会在组件挂载后自动开始动画,从起始数值翻滚到目标数值:
<van-rolling-text :start-num="0" :target-num="123" />这里有两个细节值得说明:
- 位数取两者较大者:从 RollingText.tsx 的源码看,数字场景下
itemLength取Math.max(startNum, targetNum)的字符串长度,即start-num="0"、target-num="123"时按 3 位渲染。 - 自动补零:起始与目标数字都会经过工具函数
padZero(定义于 utils/format.ts)按itemLength补齐前导零,所以0会被渲染成000,再翻滚到123,视觉效果整齐一致。
设置翻滚方向:down 与 up
direction属性控制数字的翻滚方向,默认值为down(向下翻滚),设置为up即向上翻滚:
<van-rolling-text :start-num="0" :target-num="432" direction="up" />对应源码中direction的类型为RollingTextDirection,取值只有'up' | 'down'(见 types.ts),通过makeStringProp('down')声明默认值。
方向在底层是如何实现的?在 RollingTextItem.tsx 中,每个数位会先根据方向构造滚动序列:
down(向下翻滚):将数字序列slice().reverse()反转,初始位置在序列底部(translateY(-总高度)),随后动画从底部滚回顶部,视觉上表现为数字自上而下滚落;up(向上翻滚):保持序列顺序,动画从顶部滚到底部。
方向还会以 BEM 修饰类van-rolling-text-item--down/--up的形式作用于根节点,配合 index.less 中两套@keyframes(van-down、van-up)驱动位移,具体动画机制见下文“动画原理”一节。
设置各数位停止顺序:ltr 与 rtl
stop-order属性控制各个数位动画的停止先后顺序,默认ltr(先停高位,即最左边先停),设置为rtl则先从个位(最右边)停止:
<van-rolling-text :start-num="0" :target-num="54321" stop-order="rtl" />其实现逻辑在 RollingText.tsx 的getDelay函数中:每一位数都有一个独立的动画延迟,延迟值由该位索引和总位数共同决定——
ltr:delay = 0.2 * i(i为从左到右的位数索引,第 0 位是最高位),高位延迟最短、最先开始也最先停止;rtl:delay = 0.2 * (len - 1 - i),最右侧的个位延迟最短,最先停止。
也就是说,“停止顺序”本质上是每位数 0.2 秒的错峰延迟,最终所有位数在各自的动画周期内完成翻转,形成依次停定的层叠视觉效果。
翻转非数字内容:text-list
text-list属性用于翻转非数字内容。组件会从数组的第一项翻滚到最后一项。使用该模式有两点约束:数组长度必须大于等于 2,且每一项的长度必须一致:
<van-rolling-text :text-list="textList" :duration="1" />import { ref } from 'vue'; export default { setup() { const textList = ref([ 'aaaaa', 'bbbbb', 'ccccc', 'ddddd', 'eeeee', 'fffff', 'ggggg', ]); return { textList }; }, };源码层面的处理方式(见 RollingText.tsx):
- 只要
textList是长度大于 0 的数组,组件即进入isCustomType(自定义文本)模式; itemLength取textList[0].length(即每个字符串的长度),每个字符串的同一索引位会组成一列可翻滚内容:getTextArrByIdx(idx)遍历数组,取出每项的第idx个字符,作为该数位的滚动序列;- 由于每一位的滚动内容是整列的字符,因此要求所有字符串长度一致,否则短字符串的越界索引会取到
undefined,破坏渲染。
自定义样式:CSS 变量与 height 属性
RollingText 提供了若干 CSS 变量用于主题定制,也支持直接修改组件样式。此外,height属性(默认40,单位px)用于设置单个数位的高度,在数字较大时可适当调大:
<van-rolling-text class="my-rolling-text" :height="54" :start-num="12345" :target-num="54321" />.my-rolling-text { --van-rolling-text-background: #1989fa; --van-rolling-text-color: white; --van-rolling-text-font-size: 24px; --van-rolling-text-gap: 6px; --van-rolling-text-item-border-radius: 5px; --van-rolling-text-item-width: 40px; }样式变量在 index.less 中以:root/:host声明默认值。height的底层作用体现在两处(见 RollingTextItem.tsx):
- 作为单项的
line-height,控制每个数字占位的高度; - 参与计算总位移:
translatePx = -(height * (序列长度 - 1)),位移距离随高度线性放大,确保无论高度多少,翻滚终态都能精准停在目标数字上。
手动控制:start 与 reset
默认组件自动播放动画,但通过auto-start属性(默认true)可以关闭自动播放,再通过 ref 获取组件实例调用start、reset方法手动控制动画:
<van-rolling-text ref="rollingTextRef" :start-num="0" :target-num="54321" :auto-start="false" /> <van-grid clickable :column-num="3"> <van-grid-item icon="play-circle-o" :text="start" @click="start" /> <van-grid-item icon="replay" :text="reset" @click="reset" /> </van-grid>import { ref } from 'vue'; export default { setup() { const rollingTextRef = ref(null); const start = () => { rollingTextRef.value.start(); }; const reset = () => { rollingTextRef.value.reset(); }; return { rollingTextRef, start, reset }; }, };从 RollingText.tsx 的源码可以看到内部状态与联动逻辑:
- 内部维护
rolling响应式标记,初始值等于props.autoStart,它决定了子组件是否挂上--animate动画类; start()仅将rolling置为true;reset()先将rolling置为false(移除动画类、回到初始位置),如果auto-start为true,则通过raf(requestAnimationFrame,来自@vant/use)在下一帧重新触发start(),实现“重置后自动重播”的效果;- 同时组件
watch了auto-start的变化:当属性从false变为true时会立即调用start(); - 两个方法通过
useExpose(见 composables/use-expose.ts)挂载到组件实例的proxy上对外暴露。
这些行为均有对应的单元测试覆盖(见 rolling-text/test/index.spec.ts):包括auto-start为true/false时动画类是否存在、调用start()后动画类出现、调用reset()后动画类消失,以及auto-start为true时调用reset()后动画会重新启动(测试中通过await later(50)等待下一帧验证)。这些测试同样可以作为你在项目中验证滚动行为的参考范式。
API 参考
Props
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| start-num | 起始数值 | number | 0 |
| target-num | 目标数值 | number | - |
| text-list | 内容数组,用于翻转非数字内容 | string[] | [] |
| duration | 动画时长,单位为秒 | number | 2 |
| direction | 文本翻滚方向,值为down和up | string | down |
| auto-start | 是否自动开始动画 | boolean | true |
| stop-order | 各个数位动画停止先后顺序,值为ltr和rtl | string | ltr |
| height | 数字高度,单位为px | number | 40 |
上述默认值与 RollingText.tsx 中rollingTextProps的声明完全一致:startNum、duration、height使用makeNumberProp声明数字类型默认值,autoStart使用truthProp声明布尔true,textList使用makeArrayProp声明数组默认值[]。其中target-num无默认值,数字场景下为必填项(源码中通过非空断言props.targetNum!使用)。
方法
通过 ref 可以获取到 RollingText 实例并调用实例方法,实例方法获取方式可参考 组件实例方法。
| 方法名 | 说明 | 参数 | 返回值 |
|---|---|---|---|
| start | 开始动画 | - | - |
| reset | 重置动画 | - | - |
类型定义
组件导出以下类型定义:
import type { RollingTextProps, RollingTextInstance, RollingTextDirection, RollingTextStopOrder, } from 'vant';RollingTextInstance是组件实例的类型,在手动控制场景下可用于类型安全的 ref,用法如下:
import { ref } from 'vue'; import type { RollingTextInstance } from 'vant'; const rollingTextRef = ref<RollingTextInstance>(); rollingTextRef.value?.start();从 types.ts 可以看到这些类型的完整定义:RollingTextDirection = 'up' | 'down'、RollingTextStopOrder = 'ltr' | 'rtl'、RollingTextExpose = { start: () => void; reset: () => void },而RollingTextInstance是ComponentPublicInstance<RollingTextProps, RollingTextExpose>,因此rollingTextRef.value上能同时获得 props 与start/reset方法的完整类型提示。
主题定制:样式变量
组件提供了下列 CSS 变量,可用于自定义样式,使用方法可参考 ConfigProvider 组件(ConfigProvider 可用于全局批量配置主题变量)。
| 名称 | 默认值 | 描述 |
|---|---|---|
| --van-rolling-text-background | inherit | 单个数位背景色 |
| --van-rolling-text-color | var(--van-text-color) | 数字颜色 |
| --van-rolling-text-font-size | var(--van-font-size-md) | 字体大小 |
| --van-rolling-text-gap | 0px | 数位之间的间隔 |
| --van-rolling-text-item-width | 15px | 单个数位宽度 |
| --van-rolling-text-item-border-radius | 0px | 单个数位边框圆角 |
这些变量在 index.less 中统一声明默认值:背景色默认inherit继承外层,文字颜色默认跟随主题的--van-text-color,字体大小默认跟随--van-font-size-md。--van-rolling-text-gap通过数位项的margin-right实现数位间距(最后一位会移除margin-right),--van-rolling-text-item-width与--van-rolling-text-item-border-radius分别控制单个数位的宽度与圆角;同时类型定义中也导出了RollingTextThemeVars,可在 TypeScript 中约束主题变量名。对应主题变量同样已在RollingTextThemeVars中声明(见 types.ts)。
动画原理深度剖析
理解 RollingText 的底层机制,有助于你预判它在复杂业务下的表现(例如异步数据变化、动画中途重触发等)。
数字滚动序列的构建
对于数字场景,每个数位需要一列“可滚动数字”。在 RollingText.tsx 的getFigureArr中,该序列由三段拼接而成:
- 从起始位数字
start递增到9; - 完整的
0到9循环CIRCLE_NUM次(源码常量CIRCLE_NUM = 2,即默认滚动两圈); - 从
0递增到目标位数字target。
例如起始个位为0、目标个位为3,该位序列即为0,1,...,9,0,1,...,9(×2),0,1,2,3。CIRCLE_NUM决定了翻滚的“圈数”,是营造真实滚动感的关键参数;位移总高度即由这一列数字的数量乘以height得出。
CSS 关键帧与位移驱动
单个数位的渲染结构(见 RollingTextItem.tsx)为:
van-rolling-text-item(外层,overflow: hidden 视窗) └── van-rolling-text-item__box(滚动容器,挂 --animate 触发动画) └── 若干 van-rolling-text-item__item(单个数字,line-height = height)滚动容器通过行内样式注入三个 CSS 自定义属性(见 RollingTextItem.tsx):
--van-translate:目标位移(-(height * (序列长度 - 1)));--van-duration:动画时长(duration秒);--van-delay:该位延迟(stop-order计算出的错峰延迟)。
动画由 index.less 中.van-rolling-text-item__box--animate触发的animation驱动:animation: van-up var(--van-duration) ease-in-out var(--van-delay),且animation-iteration-count: 1(只播放一次)、animation-fill-mode: both(结束后保持终态)。
两套关键帧对应两个翻滚方向:
@keyframes van-up:从translateY(0)过渡到translateY(var(--van-translate)),配合未反转的序列,呈现数字向上滚动;@keyframes van-down:从translateY(var(--van-translate))过渡到translateY(0),配合反转后的序列,呈现数字向下滚动。
为什么“非数字文本”也能滚动
非数字模式复用了完全相同的动画管线:getTextArrByIdx构造出的字符序列同样交给RollingTextItem,每个字符占据height高的格子,通过相同的--van-translate位移完成整列翻滚。这也是“非数字翻转”与“数字翻转”在代码层面仅差一个序列构造函数的根本原因。
适用场景与注意事项
RollingText 适合以下场景:
- 大屏/统计页面的数字播报(播放量、订单量、金额等),配合
duration调节节奏; - 需要循环展示的短文本切换,通过
text-list+auto-start手动触发; - 数据变化时重新翻滚,可结合
reset()与auto-start的组合行为实现“重置后自动重播”。
使用时注意几点:
text-list模式要求数组长度 ≥ 2 且每项等长;target-num在数字模式下必须提供(类型为非可选,源码中使用非空断言);- 数字位数以
start-num/target-num中较大者为准,较小的数值会被padZero补零; - 动画默认自动播放,需要等待用户操作或异步数据就绪时,记得设置
:auto-start="false"并配合实例方法触发; - 所有主题变量均可通过 CSS 变量覆盖,也可借助 ConfigProvider 进行全局主题统一管理。
完整的可运行演示可参考 rolling-text/demo/index.vue,其中覆盖了基础用法、双向翻滚、个位先停、非数字文本、自定义样式与手动控制六种场景,并配合按钮触发动画,是快速上手本组件的最佳样例。
【免费下载链接】vantA lightweight, customizable Vue UI library for mobile web apps.项目地址: https://gitcode.com/GitHub_Trending/va/vant
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考