wui
组件

文本扰动解码 Text Scramble

通过随机字符的高频采样闪烁并按时序平滑收敛,营造黑客终端、密码破译与数字解密质感的文本动效组件。

第三方依赖 · motion

基础用法

最简单的文本扰动解码用法。触发时,随机字符快速跳变并在指定时长内平滑收敛锁定为目标文案:

Loading…

安装与引入

通过 CLI 自动添加组件,或手动复制源码至项目中:

pnpm dlx @wui-design/cli@latest add @wui/text-scramble
安装依赖与 Motion 动效库
pnpm add motion clsx tailwind-merge
复制组件源码到 components/ui/text-scramble.tsx
components/ui/text-scramble.tsx
"use client"

import * as React from "react"
import { useReducedMotion } from "motion/react"

import { cn } from "@/lib/utils"

const defaultCharacterSet =
  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%&*"

export interface TextScrambleProps extends React.ComponentProps<"p"> {
  /** Final text revealed by the scramble. */
  children: string
  /** HTML element rendered by the component. @default "p" */
  as?: React.ElementType
  /** Total reveal duration in seconds. @default 0.8 */
  duration?: number
  /** Seconds between scramble frames. @default 0.04 */
  speed?: number
  /** Characters sampled while scrambling. */
  characterSet?: string
  /** Start the effect. Toggling false then true replays it. @default true */
  trigger?: boolean
  /** Called after every character reaches its final value. */
  onScrambleComplete?: () => void
}

/** Resolves randomized characters into the supplied text. */
function TextScramble({
  children,
  as = "p",
  duration = 0.8,
  speed = 0.04,
  characterSet = defaultCharacterSet,
  trigger = true,
  onScrambleComplete,
  className,
  ...props
}: TextScrambleProps) {
  const Component = as
  const reduceMotion = useReducedMotion()
  const [display, setDisplay] = React.useState(children)
  const onCompleteRef = React.useRef(onScrambleComplete)

  React.useEffect(() => {
    onCompleteRef.current = onScrambleComplete
  })

  React.useEffect(() => {
    if (!trigger || reduceMotion) {
      setDisplay(children)
      return
    }

    const startedAt = performance.now()
    const frameDuration = Math.max(speed * 1000, 16)
    const totalDuration = Math.max(duration * 1000, frameDuration)
    const timer = window.setInterval(() => {
      const progress = Math.min(
        (performance.now() - startedAt) / totalDuration,
        1
      )
      const resolved = Math.floor(progress * Array.from(children).length)
      setDisplay(
        Array.from(children)
          .map((character, index) => {
            if (/\s/.test(character) || index < resolved) return character
            return (
              characterSet[Math.floor(Math.random() * characterSet.length)] ??
              character
            )
          })
          .join("")
      )
      if (progress >= 1) {
        window.clearInterval(timer)
        setDisplay(children)
        onCompleteRef.current?.()
      }
    }, frameDuration)

    return () => window.clearInterval(timer)
  }, [characterSet, children, duration, reduceMotion, speed, trigger])

  return (
    <Component
      data-slot="text-scramble"
      className={cn("font-mono", className)}
      {...props}
    >
      <span className="sr-only">{children}</span>
      <span aria-hidden="true">{display}</span>
    </Component>
  )
}

export { TextScramble }

属性 Props

TextScramble 支持以下配置属性,并继承底层渲染 HTML 元素(默认 <p>)的原生属性:

属性类型默认值说明
childrenstring—解码完成后最终呈现的目标纯文本字符串。
durationnumber0.8从开始闪烁到全文字符完全收敛锁定的总持续时间(单位:秒)。
speednumber0.04随机字符每次刷新的时间间隔(单位:秒),数值越小闪烁越频繁。
characterSetstring"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%&*"扰动过程中随机采样的候选字符集字符串。
triggerbooleantrue控制动效的触发状态。重置为 false 后再次变为 true 即可重播解码动效。
asReact.ElementType"p"外层容器所渲染的语义 HTML 标签(如 span, p, h3, div 等)。
classNamestring—应用于外层容器的额外 CSS 类名(默认内置包含 font-mono)。

事件 Events

属性类型默认值说明
onScrambleComplete() => void—当整段文字的所有字符全部完成解码并锁定为最终值时触发的回调函数。回调通过 ref 读取,传入内联函数不会导致动画重新开始。

使用场景与设计规范

TextScramble 专为极客科技、AI 运算、安全防护与数据大屏等硬核场景设计。

  • 使用等宽字体(Monospace):扰动过程中字符频繁随机替换,若使用变宽字体可能造成严重的水平抖动。组件默认内置 font-mono,请确保目标容器维持等宽排版。
  • 合理设置持续时间:推荐将 duration 控制在 0.6s ~ 1.2s 之间。过短无法看清解密细节,过长则会延误用户获取关键信息的效率。
  • 智能保留空白符号:组件内部算法自动识别并保留原始文本中的空格与换行符,不会用随机字符填充空格位置,从而保持段落结构稳定。

场景示例

自定义字符采样集

根据不同业务风格挑选合适的采样字符集,如二进制(0/1)、十六进制哈希或标点符号集合:

Loading…

密钥解密与一键复制

在开发者控制台展示敏感 Token 或 API Key 时,提供极具仪式感的解密动效:

Loading…

集群遥测状态解码

在系统服务监控面板中,文本变化时自动重新解码,并在 onScrambleComplete 中切换状态指示点:

Loading…

无障碍与交互 Accessibility

  • 完整的屏幕阅读器直读:组件内部保留一份视觉隐藏(sr-only)的最终文本,内部跳动的扰动字符包装在 aria-hidden="true" 的 span 标签中。辅助技术将立即把最终真实文案朗读给视障用户,绝不会朗读瞬态的无意义随机乱码。
  • 系统减少动态偏好(Reduced Motion):在系统启用了 prefers-reduced-motion 时,组件将直接跳过定时器与字符随机化逻辑,立即渲染最终纯文本。