🚀 学习笔记
2026-06-15 · 约6分钟阅读

Framer Motion 动画性能优化:大屏 60fps 实践

项目实战 前端 Framer Motion

问题背景

抖音数据大屏用了大量 Framer Motion 动画:数字滚动、地图下钻过渡、卡片悬浮效果。开发环境跑得挺好,但部署到客户老电脑上,风扇狂转,帧率掉到 30fps 以下。

性能分析

用 Chrome DevTools 的 Performance 面板录了一波,发现两个主要问题:

解决方案

1. 只用 transform 和 opacity

这两个属性不会触发 layout/paint,只触发 composite,GPU 直接处理:

// 错误:改 width/height,触发 layout
<motion.div
  animate={{ width: isOpen ? 200 : 0 }}
/>

// 正确:用 transform: scaleX
<motion.div
  animate={{ scaleX: isOpen ? 1 : 0 }}
  style={{ transformOrigin: "left" }}
/>

// 错误:改 top/left
animate={{ top: y, left: x }}

// 正确:用 transform
animate={{ x, y }}

2. 用 willChange: "transform" 提前告诉浏览器

<motion.div
  style={{ willChange: "transform", transform: "translateZ(0)" }}
  animate={{ x: 100 }}
/>
// translateZ(0) 触发硬件加速,提前创建合成层

3. 懒加载 + 虚拟滚动处理大数据量动画

地图下钻时不给所有区县同时加动画,只给可视区域内的加:

// 用 IntersectionObserver 判断是否在视口内
const [visible, setVisible] = useState(false);
const ref = useRef(null);

useEffect(() => {
  const observer = new IntersectionObserver(
    ([entry]) => setVisible(entry.isIntersecting),
    { threshold: 0.1 }
  );
  observer.observe(ref.current);
  return () => observer.disconnect();
}, []);

return (
  <motion.div
    ref={ref}
    animate={visible ? { opacity: 1, y: 0 } : {}}
    transition={{ duration: 0.3 }}
  >
    {children}
  </motion.div>
);

4. 用 transition={{ type: false }} 禁用物理弹簧

Framer Motion 默认用 spring 动画,物理计算很耗性能。数据大屏不需要弹性效果,用 tween 就行:

// 错误:默认 spring,性能开销大
<motion.div animate={{ x: 100 }} />

// 正确:用 tween,性能更好
<motion.div
  animate={{ x: 100 }}
  transition={{ type: "tween", duration: 0.3, ease: "easeOut" }}
/>

5. 用 useTransform 和 useSpring 代替 animate

对于高频更新的动画(比如实时数据滚动),用 Framer Motion 的 value API 直接操作,跳过 React 渲染周期:

import { useSpring, useTransform } from "framer-motion";

function AnimatedNumber({ value }) {
  const springValue = useSpring(0, { stiffness: 300, damping: 30 });
  const display = useTransform(springValue, v => Math.round(v));

  useEffect(() => {
    springValue.set(value);
  }, [value]);

  return <motion.span>{display}</motion.span>;
}
✅ 最终效果:优化后,同时动画元素从 3000+ 降到视口内约 50 个,帧率稳定在 58-60fps,客户老电脑也不卡了。

快速检查清单