1 分钟阅读
数字滚动动画的实现与性能优化556
#动效#Framer Motion#性能

数字滚动动画有一种奇妙的魅力:它不是简单地把数字换掉,而是让「变化」本身被看见。就像机械翻页钟,每一次翻动都在提醒你——时间在流逝,数据在更新。
这篇文章,我想把我在几个项目里反复打磨的数字滚动组件拆开来讲。
核心原理
数字滚动的本质,是让新旧两个状态在垂直方向上做位移过渡。用 AnimatePresence 的 popLayout 模式,可以让旧数字滚出、新数字滚入,且不破坏文档流。
function RollDigit({ char }: { char: string }) {
return (
<span className="relative inline-block h-[1em] w-[0.62em] overflow-hidden">
<AnimatePresence initial={false} mode="popLayout">
<motion.span
key={char}
initial={{ y: '100%', opacity: 0 }}
animate={{ y: '0%', opacity: 1 }}
exit={{ y: '-100%', opacity: 0 }}
transition={{ duration: 0.5, ease: [0.16, 1, 0.3, 1] }}
>
{char}
</motion.span>
</AnimatePresence>
</span>
);
}关键点在于外层容器的 overflow-hidden 与固定尺寸:它像一个窗口,只露出当前数字,滚动的过程发生在窗口之外。
逐位滚动
如果要显示的是「12」而不是单个字符,就把它拆成每一位:
const chars = String(value).split('');每一位独立滚动,天然形成「个位先进位」的机械感。为了让数字宽度稳定,我用等宽字体(JetBrains Mono),并给每一位固定的 width,避免滚动时布局抖动。
性能优化
动画的性能预算,决定了它会不会掉帧。我遵循三条规则:
- 只动
transform和opacity——它们是合成器属性,不触发重排。 - 避免逐帧更新 React 状态——位移交给 motion 的内联
style,React 不做无谓的重渲染。 - 尊重
prefers-reduced-motion——对前庭敏感的访客,直接显示最终值。
const reduced = useReducedMotion();
if (reduced) return <span>{value}</span>;结语
好的动效不是炫技,而是让信息的变化「可感知」。数字滚动之所以高级,恰恰因为它克制——只在必要的时刻,用最小的动作,讲清一件事。
动画的终点,是让用户忘记动画的存在。