Skyroc Native UI

RollingText

数字 / 文本翻滚动画

翻滚文本(RollingText)把数字或文本按列逐字符滚动到目标值,常用于计数、榜单名次、抽奖开奖。每一列是一条独立的字符序列,各列只错开起跑时间、不改变时长,因此看上去是依次落定。

import { RollingText } from '@skyroc/native-ui';

基础用法

数字模式下 targetNum 是目标值,列数由它的位数决定;startNum首次播放的起点。默认挂载后自动播放。

RollingTextBasic.tsx
Loading…
import { RollingText } from '@skyroc/native-ui';
import { View } from 'react-native';

const RollingTextBasic = () => {
  return (
    <View className="bg-background items-center px-4 py-6">
      <RollingText
        startNum={0}
        targetNum={123}
      />
    </View>
  );
};

export { RollingTextBasic };

targetNum 的异常值会被归一化:负数取绝对值、小数向零取整、非有限值退化为 0、超过 Number.MAX_SAFE_INTEGER 按上限处理 —— 1e21 这类值 String 出来是 "1e+21",逐位拆开会滚出一列乱码。

何时使用

  • 数值变化需要被看见:积分、金额、在线人数。
  • 开奖、抽签这类「逐位落定」的仪式感场景(用 textList 文本模式)。
  • 纯展示的静态数字请直接用 Text

滚动方向

direction 控制字符向下(默认)或向上滚入,两种方向的目标值计算完全一致。

RollingTextDirection.tsx
Loading…
import { RollingText, Text } from '@skyroc/native-ui';
import { View } from 'react-native';

const RollingTextDirection = () => {
  return (
    <View className="flex-row justify-around bg-background px-4 py-6">
      <View className="items-center gap-2">
        <Text color="muted">direction="down"</Text>
        <RollingText
          direction="down"
          startNum={0}
          targetNum={123}
        />
      </View>
      <View className="items-center gap-2">
        <Text color="muted">direction="up"</Text>
        <RollingText
          direction="up"
          startNum={0}
          targetNum={456}
        />
      </View>
    </View>
  );
};

export { RollingTextDirection };

停止顺序

stopOrder 决定哪一列先停:ltr 左列先起跑也先停(默认),rtl 反之。delayStep 是相邻两列的启动间隔(默认 200ms),两者共同决定错落效果。

RollingTextStopOrder.tsx
Loading…
import { RollingText, Text } from '@skyroc/native-ui';
import { View } from 'react-native';

const RollingTextStopOrder = () => {
  return (
    <View className="flex-row justify-around bg-background px-4 py-6">
      <View className="items-center gap-2">
        <Text color="muted">ltr</Text>
        <RollingText
          delayStep={300}
          startNum={0}
          stopOrder="ltr"
          targetNum={123}
        />
      </View>
      <View className="items-center gap-2">
        <Text color="muted">rtl</Text>
        <RollingText
          delayStep={300}
          startNum={0}
          stopOrder="rtl"
          targetNum={789}
        />
      </View>
    </View>
  );
};

export { RollingTextStopOrder };

onFinish 只由最后落定的那一列触发一次,不会每列各报一次。

动画节奏

属性含义默认值
circles数字模式下每列额外空转的圈数,夹在 0 ~ 102
duration单列滚动时长(毫秒),不含 delayStep 的错峰耗时2000
height每个字符所占的行高,字符的 lineHeight 跟随此值40
RollingTextDuration.tsx
Loading…
import { RollingText } from '@skyroc/native-ui';
import { View } from 'react-native';

const RollingTextDuration = () => {
  return (
    <View className="bg-background items-center px-4 py-6">
      <RollingText
        circles={0}
        delayStep={400}
        duration={1200}
        height={48}
        startNum={0}
        targetNum={9999}
      />
    </View>
  );
};

export { RollingTextDuration };

各列共用同一个 duration,因此序列更长的列滚得更快 —— 这样它们才会按 stopOrder 的节奏依次停下,而不是长的那列拖到最后。

文本模式

textList 即进入文本模式:首项是起始态、末项是最终态,依次滚过中间各项。列数取最长一项,短项在多出的列上留空。文本模式下 targetNum / startNum / circles 不可用(类型上就是 never)。

RollingTextTextMode.tsx
Loading…
import { Button, RollingText } from '@skyroc/native-ui';
import type { RollingTextRef } from '@skyroc/native-ui';
import { useRef } from 'react';
import { View } from 'react-native';

const RollingTextTextMode = () => {
  const textRollingRef = useRef<RollingTextRef>(null);

  return (
    <View className="bg-background items-center px-4 py-6">
      <RollingText
        ref={textRollingRef}
        textList={['', '处理中', '已完成']}
      />
      <View className="mt-3 flex-row gap-2">
        <Button
          size="sm"
          variant="outline"
          onPress={() => textRollingRef.current?.reset()}
        >
          重新播放
        </Button>
      </View>
    </View>
  );
};

export { RollingTextTextMode };

手动控制与完成事件

autoStart={false} 时挂载后停在起始位置,由 ref 驱动:

方法说明
start()从起始位置播放一次;播放中调用会立即重播
reset()回到起始位置;autoStarttrue 时立即重播,否则停在起点
RollingTextManual.tsx
Loading…
import { Button, RollingText, Text } from '@skyroc/native-ui';
import type { RollingTextRef } from '@skyroc/native-ui';
import { useRef, useState } from 'react';
import { View } from 'react-native';

const RollingTextManual = () => {
  const rollingRef = useRef<RollingTextRef>(null);
  const [finishCount, setFinishCount] = useState(0);

  function handleFinish() {
    setFinishCount(current => current + 1);
  }

  function reset() {
    rollingRef.current?.reset();
  }

  function start() {
    rollingRef.current?.start();
  }

  return (
    <View className="bg-background items-center px-4 py-6">
      <RollingText
        ref={rollingRef}
        autoStart={false}
        startNum={0}
        targetNum={5678}
        onFinish={handleFinish}
      />
      <Text
        className="mt-2"
        color="muted"
      >
        onFinish 次数:{finishCount}
      </Text>
      <View className="mt-3 flex-row gap-2">
        <Button
          size="sm"
          onPress={start}
        >
          开始
        </Button>
        <Button
          size="sm"
          variant="outline"
          onPress={reset}
        >
          重置
        </Button>
      </View>
    </View>
  );
};

export { RollingTextManual };

onFinish 在最后一列落定时触发一次;被下一轮播放打断则不触发。

动态目标值

targetNum 变化后从上一轮的终点继续往下滚,而不是闪回 startNum 再来一遍。位数增加或减少时整排列会重建(key 带上列数),不会复用按旧行高定位的动画值;列数只看目标值,所以数值变短不会留下一排前导零。

RollingTextDynamic.tsx
Loading…
import { Button, RollingText, Text } from '@skyroc/native-ui';
import { useState } from 'react';
import { View } from 'react-native';

const TARGETS = [8, 42, 2026, 7];

const RollingTextDynamic = () => {
  const [targetIndex, setTargetIndex] = useState(0);

  const targetNum = TARGETS[targetIndex];

  function changeTarget() {
    setTargetIndex(current => (current + 1) % TARGETS.length);
  }

  return (
    <View className="items-center gap-3 bg-background px-4 py-6">
      <RollingText targetNum={targetNum} />
      <Text color="muted">targetNum:{targetNum}</Text>
      <Button
        size="sm"
        variant="outline"
        onPress={changeTarget}
      >
        切换目标值
      </Button>
    </View>
  );
};

export { RollingTextDynamic };

还没播过时(autoStart={false} 且没手动 start)改 targetNum 只换终点、不起跑。

样式覆盖

className 追加到根容器上,classNames 按 slot 细粒度覆盖:

slot作用位置
root根容器(横向排布)
text每个滚动字符的文字类名
RollingTextStyles.tsx
Loading…
import { RollingText } from '@skyroc/native-ui';
import { View } from 'react-native';

const RollingTextStyles = () => {
  return (
    <View className="items-center bg-background px-4 py-6">
      <RollingText
        className="gap-1 rounded-xl bg-primary/10 px-3"
        classNames={{ text: 'text-2xl font-bold text-primary' }}
        height={48}
        startNum={100}
        targetNum={2026}
      />
    </View>
  );
};

export { RollingTextStyles };

改字号时记得同步调 height:行高由 height 决定,字号和行高对不上会看到字符被裁切。

API

RollingText

RollingTextProps 是数字模式与文本模式的联合:传了 textList 即文本模式,此时 targetNum / startNum / circles 不可用。

属性说明类型默认值
targetNum目标数字(数字模式必填),列数由位数决定;负数取绝对值、小数向零取整number-
startNum首次播放的起点;之后以上一轮终点为起点number0
circles每列额外空转的圈数,夹在 0 ~ 10number2
textList文本模式的文本序列,首项为起始态、末项为最终态;列数取最长一项string[]-
direction滚动方向'down' | 'up''down'
stopOrder停止顺序,ltr 为左列先停'ltr' | 'rtl''ltr'
delayStep相邻两列的启动间隔(毫秒)number200
duration单列滚动时长(毫秒)number2000
height每个字符所占的行高number40
autoStart挂载后是否立即播放,同时决定 reset() 之后是否自动重新开始booleantrue
onFinish最后一列落定时触发一次;被下一轮打断则不触发() => void-
className根容器类名,合并在 classNames.root 之后string-
classNames各 slot 的类名覆盖,见「样式覆盖」一节SlotClassNames<RollingTextSlots>-
ref命令式控制的 ref,用于 start / resetRef<RollingTextRef>-

类型

import type {
  RollingTextDirection,
  RollingTextNumberProps,
  RollingTextProps,
  RollingTextRef,
  RollingTextSlots,
  RollingTextStopOrder,
  RollingTextTextProps
} from '@skyroc/native-ui';

RollingTextPropsRollingTextNumberProps | RollingTextTextProps 的联合,两种模式的互斥字段在类型上标成了 never

RollingTextDirection

滚动方向。

'down' | 'up'

RollingTextStopOrder

停止顺序,ltr 为左列先停。

'ltr' | 'rtl'

RollingTextSlots

可通过 classNames 覆盖的 slot 名称。

'root' | 'text'

SlotClassNames

classNames 的取值形态:把 slot 名映射到类名,每个 slot 都可选。本页用到的 slot 见 RollingTextSlots。

Partial<Record<Slots, string>>

RollingTextRef

组件实例暴露的命令式方法。

字段类型说明
start*() => void从起始位置播放一次,播放中调用会立即重播。
reset*() => void回到起始位置;autoStart 为 true 时立即重播,否则停在起点。