
本文详解如何使用 framer motion 对文本进行逐字动画时,正确保留原始空格(包括单词间空格),避免字符粘连,并提供可直接复用的解决方案与最佳实践。
本文详解如何使用 framer motion 对文本进行逐字动画时,正确保留原始空格(包括单词间空格),避免字符粘连,并提供可直接复用的解决方案与最佳实践。
在使用 framer-motion 实现逐字动画时,一个常见陷阱是:对字符串调用 .split('') 后直接映射渲染,会导致 HTML 默认的空白符折叠行为生效——即多个连续空格、换行或制表符被压缩为单个空格,而更关键的是,相邻
✅ 正确做法是 显式保留空格语义,推荐以下两种稳健方案:
方案一:使用 whiteSpace: 'pre' + 清除模板空白(推荐)
为每个 motion.div 设置 CSS whiteSpace: 'pre',使其像
标签一样保留所有空白符(含空格、换行、制表符)。同时需移除 JSX 中因格式化产生的额外空格,避免干扰:</p><pre class="brush:php;toolbar:false;">import { motion } from 'framer-motion';
type Props = {
text: string;
};
function AnimatedText({ text }: Props) {
const characters = text.split('');
return (
<div className="flex">
{characters.map((char, index) => (
<motion.div
key={index}
className="inline-block"
style={{ whiteSpace: 'pre' }} // ✅ 关键:保留空格语义
initial={{ opacity: 0, y: 15 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.04 * index }}
>
{char}
</motion.div>
))}
</div>
);
}
export default AnimatedText;
⚠️ 注意:务必移除
{char} 周围的换行与缩进(如写成 {char} 紧贴标签),否则 JSX 解析会插入额外空格节点。上述代码中使用 className="inline-block" 是为了确保视觉流式排列,避免 div 默认块级行为导致换行。
方案二:将空格替换为 (非空格字符)
若需更高兼容性或规避 CSS 白空间策略,可预处理字符串,将普通空格 ' ' 替换为 HTML 不间断空格实体 '\u00A0'(即 ):
const characters = text
.split('')
.map(char => char === ' ' ? '\u00A0' : char);再配合默认样式即可,无需额外 CSS。但注意:此法仅解决空格,不处理制表符或换行符;且 在语义上不可被屏幕阅读器自然识别为分隔符,对无障碍支持稍弱。
总结与建议
- 首选方案一:whiteSpace: 'pre' 简洁、语义清晰、支持全类型空白符;
- 避免依赖父容器 whiteSpace: 'pre-wrap' —— 它会影响整个文本流,而非单个字符;
- 如需支持多行文本动画,建议结合 display: 'inline-block' 与 verticalAlign: 'top' 防止基线错位;
- 动画性能敏感场景下,可考虑用 替代
减少 DOM 节点开销(motion.span 同样有效)。
通过精准控制空白符渲染行为,你既能享受 Framer Motion 强大的逐帧控制力,又能完全忠于原文本的排版意图。










