跳转到主要内容
返回文章
1 分钟阅读

数字滚动动画的实现与性能优化556

#动效#Framer Motion#性能
数字滚动动画的实现与性能优化556

1 数字滚动动画有一种奇妙的魅力:它不是简单地把数字换掉,而是让「变化」本身被看见。就像机械翻页钟,每一次翻动都在提醒你——时间在流逝,数据在更新。

这篇文章,我想把我在几个项目里反复打磨的数字滚动组件拆开来讲。

核心原理

数字滚动的本质,是让新旧两个状态在垂直方向上做位移过渡。用 AnimatePresencepopLayout 模式,可以让旧数字滚出、新数字滚入,且不破坏文档流。

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,避免滚动时布局抖动。

性能优化

动画的性能预算,决定了它会不会掉帧。我遵循三条规则:

  1. 只动 transformopacity——它们是合成器属性,不触发重排。
  2. 避免逐帧更新 React 状态——位移交给 motion 的内联 style,React 不做无谓的重渲染。
  3. 尊重 prefers-reduced-motion——对前庭敏感的访客,直接显示最终值。
const reduced = useReducedMotion();
if (reduced) return <span>{value}</span>;

结语

好的动效不是炫技,而是让信息的变化「可感知」。数字滚动之所以高级,恰恰因为它克制——只在必要的时刻,用最小的动作,讲清一件事。

动画的终点,是让用户忘记动画的存在。

假作真时真亦假,无为有处有还无。

凯 · KAI000%
泪蛋蛋掉在酒杯杯里
DJ版 · 镐天
0:000:00