wui
组件

文本波光 Text Shimmer Wave

逐字符传播的立体位移与高光波浪。

第三方依赖 · motion

基础用法

Loading…
pnpm dlx @wui-design/cli@latest add @wui/text-shimmer-wave
components/ui/text-shimmer-wave.tsx
"use client"

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

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

export interface TextShimmerWaveProps extends React.ComponentProps<"span"> {
  /** Text animated as a traveling character wave. */
  children: string
  /** HTML element rendered by the component. @default "span" */
  as?: React.ElementType
  /** Seconds for one full wave. @default 1.5 */
  duration?: number
  /** Z-axis travel in pixels. @default 10 */
  zDistance?: number
  /** Horizontal travel in pixels. @default 2 */
  xDistance?: number
  /** Vertical travel in pixels. @default -2 */
  yDistance?: number
  /** Distance between character wave phases. @default 1 */
  spread?: number
  /** Peak character scale. @default 1.1 */
  scaleDistance?: number
  /** Peak Y-axis rotation in degrees. @default 10 */
  rotateYDistance?: number
  /** Motion transition merged into every character. */
  transition?: Transition
}

/** Sends a shimmer-like 3D wave through text, one character at a time. */
function TextShimmerWave({
  children,
  as = "span",
  duration = 1.5,
  zDistance = 10,
  xDistance = 2,
  yDistance = -2,
  spread = 1,
  scaleDistance = 1.1,
  rotateYDistance = 10,
  transition,
  className,
  ...props
}: TextShimmerWaveProps) {
  const Component = as
  const reduceMotion = useReducedMotion()

  return (
    <Component
      data-slot="text-shimmer-wave"
      className={cn("inline-flex [perspective:400px]", className)}
      {...props}
    >
      <span className="sr-only">{children}</span>
      {Array.from(children).map((character, index) => (
        <motion.span
          aria-hidden="true"
          data-slot="text-shimmer-wave-character"
          className="text-muted-foreground inline-block whitespace-pre"
          key={`${character}-${index}`}
          animate={
            reduceMotion
              ? undefined
              : {
                  color: [
                    "var(--muted-foreground)",
                    "var(--foreground)",
                    "var(--muted-foreground)",
                  ],
                  x: [0, xDistance, 0],
                  y: [0, yDistance, 0],
                  z: [0, zDistance, 0],
                  scale: [1, scaleDistance, 1],
                  rotateY: [0, rotateYDistance, 0],
                }
          }
          transition={{
            duration,
            ease: "easeInOut",
            repeat: Infinity,
            delay: (index * duration * 0.08) / Math.max(spread, 0.1),
            ...transition,
          }}
        >
          {character === " " ? "\u00a0" : character}
        </motion.span>
      ))}
    </Component>
  )
}

export { TextShimmerWave }

使用场景

TextShimmerWave 把颜色、位移、缩放和轻微旋转组合成逐字符波浪。它比普通扫光更醒目,适合短暂生成状态或单一展示焦点; 不要在同一视口放置多个波浪文本。

属性

属性类型默认值说明
children *string—按字符播放立体波浪动画的文字。
asElementType<any, keyof IntrinsicElements>span组件实际渲染的 HTML 元素。
durationnumber1.5完整波浪动画循环的时长,单位为秒。
zDistancenumber10字符沿 Z 轴移动的距离,单位为像素。
xDistancenumber2字符沿水平方向移动的距离,单位为像素。
yDistancenumber-2字符沿垂直方向移动的距离,单位为像素。
spreadnumber1相邻字符波浪相位之间的距离。
scaleDistancenumber1.1字符达到的最大缩放比例。
rotateYDistancenumber10字符达到的最大 Y 轴旋转角度,单位为度。
transitionTransition—合并到每个字符上的过渡动画。

属性名后的 * 表示必填。

事件

组件没有自有事件,底层元素事件继续透传。用户开启“减少动态效果”时只显示静态文字。

扩展用法

xDistance、yDistance、zDistance 控制空间位移,scaleDistance 和 rotateYDistance 控制形变。使用自定义 transition 时, 建议保持较小的位移和旋转,避免文字难以阅读。

无障碍

组件内部保留一份视觉隐藏(sr-only)的完整文本,逐字符动画的 span 对读屏工具隐藏,整句只会被朗读一次。