返回效果库

文字逐字浮现

Staggered Text

让一句话从模糊里一个字一个字浮出来

完整版:适合从零新建组件,规格、契约、验收全都在。

实时预览

文字

点「重播」再看一次检测到「减少动态效果」,动效已降级

参数

边调边看,提示词同步更新。

角色

你是一名资深前端工程师,工作在现有的项目中,技术栈是 React 19 + TypeScript + Tailwind CSS v4 + Motion v14(motion 包)。只实现下面这一个微交互:不要加额外功能,不要顺手重构周围代码。

任务

实现文字逐字浮现:把文字拆成单元(中文单字、英文单词),每个单元从下方 22px、6px 模糊中浮现到位——单元时长 520ms、相邻间隔 45ms,缓动 cubic-bezier(0.22, 1, 0.36, 1)。

文件契约

- 只新建一个文件:components/effects/StaggeredText.tsx。

- 依赖:项目已安装 motion@14,从 motion/react 引入。不要新增任何其他包。

- 不要修改 package.json、全局 CSS、Tailwind 配置或任何其他已有文件。

实现要点

- 文件顶部加 "use client"。

- 拆分规则:CJK 按单字、拉丁文按单词、空格转成 \u00A0、换行渲染成 block 行;导出 StaggeredText,props 为 text、stagger、duration、rise、blur、className。

- 用 motion.span 的 initial / animate / transition,ease 用 [0.22, 1, 0.36, 1],delay = index * stagger / 1000。

- reduced-motion 用 CSS 媒体查询把单元强制到最终状态(.staggered-unit + !important),不要用 JS 分支切换 DOM——否则 SSR 首帧会和客户端不一致;容器加 aria-label(完整文本),动画 span 加 aria-hidden。

规格 —— 精确数值,不许四舍五入或“优化”

- 拆分规则:每个中文字符是一个单元;连续拉丁文保持为单词;空格转成 \u00A0(保证 inline-block 单元里保留宽度);每个 \n 渲染成一个 block 行。

- 每个单元:初始 opacity 0、translateY(22px)、filter: blur(6px);动画到 opacity 1、translateY(0)、blur(0)。

- 时间:单元时长 520ms,延迟 = 单元序号 × 45ms,缓动 cubic-bezier(0.22, 1, 0.36, 1)(不要用 linear)。

- 字体:40px、行高 1.15、字重 600、字距 -0.03em、颜色 #EDEEF0、最大宽度 640px。

- 布局:单元是 inline-block 且 white-space: pre;整段只布局一次,动画结束后不允许有任何位移。

- 无障碍:容器带 aria-label(完整文本),每个动画单元 aria-hidden。

- 重播:组件在 mount 时播放动画;父级用新的 key 重新挂载来重播。不要留下无法清理的定时器。

行为

- mount 时播放一次(仅客户端);不要绑定滚动触发,不要 IntersectionObserver。

- 动画必须从第一帧开始:单元以初始状态声明,不能先闪出最终态。

- 用新的 key 重新挂载组件时,必须按同样的时间参数重新播放。

- 屏幕阅读器只读一次完整文本,而不是逐个单元念;文字要保持可选中。

- prefers-reduced-motion: reduce:跳过动画,直接渲染最终文字。

- 零布局位移:段落从第一帧就保持最终的行数与高度,单元动画结束后不能换行。

- 卸载时清理动画;不要在卸载后更新状态或留下定时器。

禁止

- 不要动画 width / height / margin / padding / letter-spacing;只允许 transform / opacity / filter。

- 不要从拉丁单词中间切开,也不要把空格丢掉。

- 不要留 TODO、不要写 // 其余代码、不要用 any、不要 console.log、不要留注释掉的实验代码。

- 不要把组件包进页面、布局、演示壳或 Provider。

- 不要改动全局 CSS、Tailwind 配置或上面文件之外的任何文件。

- 除 Motion 外不要引入任何动画库,更不要用 GSAP。

验收 —— 回答前逐项自查

1. 中文按单字、英文按单词拆分;空格与换行都保留。

2. 单元按顺序入场,相邻单元间隔正好是 stagger,单元时长符合配置。

3. 第一帧就是初始状态(透明度 0、偏移、模糊),不会先闪出最终态。

4. 零布局位移:动画前后行数与最终高度完全一致。

5. 屏幕阅读器只读一次完整文本(aria-label),span 为 aria-hidden,文字可选中。

6. prefers-reduced-motion 下不播动画,直接显示最终文字。

7. 换 key 重新挂载后能干净重播;没有定时器/监听泄漏;渲染数值与规格一致。

输出

- 用一个代码块输出完整文件内容,不要 diff、不要省略号,代码块之前不要解释。

- 如果缺少必要依赖,先停下来问我,不要擅自安装。

- 代码块之后最后输出一行:self-check: 1 ✓ · 2 ✓ · 3 ✓ · 4 ✓ · 5 ✓ · 6 ✓ · 7 ✓,未通过的项用 ✗ 并附一句原因。

与演示同源生成2,401 字符 · 667 约 tokens · 56 行
技术规格

把标题按「中文单字 / 英文单词」拆分,每个单元从下方 22px、6px 模糊里浮现,按 45ms 的间隔依次入场。整段文字只占一次布局,动画结束后不留位移。

逐字间隔
45 ms
单字时长
520 ms
上浮距离
22 px
初始模糊
6 px
拆分规则
中文单字 / 英文单词
缓动
cubic-bezier(0.22, 1, 0.36, 1)
节奏
45ms 间隔 · 520ms 单元
字号
40px · 600
行高
1.15
无障碍
aria-label 全文 + span aria-hidden
验收清单

提示词要求 AI 在输出末尾逐条自检。

  1. 中文按单字、英文按单词拆分;空格与换行都保留。
  2. 单元按顺序入场,相邻单元间隔正好是 stagger,单元时长符合配置。
  3. 第一帧就是初始状态(透明度 0、偏移、模糊),不会先闪出最终态。
  4. 零布局位移:动画前后行数与最终高度完全一致。
  5. 屏幕阅读器只读一次完整文本(aria-label),span 为 aria-hidden,文字可选中。
  6. prefers-reduced-motion 下不播动画,直接显示最终文字。
  7. 换 key 重新挂载后能干净重播;没有定时器/监听泄漏;渲染数值与规格一致。

待回归验证提示词与演示已同源生成;尚未在真实 Cursor / Claude Code 项目中做回归。

提示词如何还原这个效果

不是把效果描写一遍,而是把效果写成一份可执行的工程需求。

数字规格
每个像素、每个时间参数都来自同一个参数源——滑块改什么,提示词就写什么。
文件契约
只允许新建指定文件、指定依赖白名单,避免它顺手改你的全局样式或装一堆包。
验收清单
提示词末尾强制 AI 自检布局位移、键盘可用、reduced-motion 等 7 项。
完整版 / 中文

React + Tailwind + Motion