用Framer Motion打造丝滑动画效果的实战经验分享
又踩坑了,Framer Motion在移动端的动画卡顿问题
最近在用Framer Motion做移动端页面的时候,遇到了一个特别头疼的问题。页面滚动时动画会出现明显的卡顿,尤其是在低端安卓机上,简直没法看。这里我踩了个坑,折腾了好几天才搞定。
一开始我以为是CSS样式写得不够优化,各种精简重绘区域、减少层级嵌套都试过了,结果还是卡。后来试了下发现原来是Framer Motion的默认配置和移动端有些冲突,尤其是touch事件这块。
三种方案对比,我选了最简单的
说下我尝试的几种解决方案:
- 第一种是完全放弃Framer Motion,改用手写的requestAnimationFrame。确实流畅了,但工作量太大,所有动画都要重写。
- 第二种是用transform替代top/left定位,这个倒是解决了部分性能问题,但复杂动画还是不行。
- 最后发现其实是Framer Motion的layout特性导致的重绘问题,调整几个参数就搞定了。
最终方案简单粗暴,核心代码就这几行:
import { motion } from "framer-motion";
const itemVariants = {
hidden: { opacity: 0, y: 50 },
visible: { opacity: 1, y: 0, transition: { duration: 0.3 } }
};
function AnimatedList() {
return (
<motion.ul layout="position">
{items.map((item) => (
<motion.li
key={item.id}
variants={itemVariants}
initial="hidden"
animate="visible"
exit="hidden"
transition={{ type: "spring", stiffness: 300, damping: 30 }}
>
{item.content}
</motion.li>
))}
</motion.ul>
);
}
核心代码就这几行,重点在这些细节
这里有几个关键点要特别注意:
- layout属性:我把layout从默认的”size”改成了”position”,这个改动立马见效。原因是size会同时触发布局和绘制,而position只会影响位置计算。
- transition配置:原本用的是默认的tween动画,在低端机上表现很糟糕。换成spring后,利用物理引擎的自然过渡效果,性能提升很明显。
- stiffness和damping:这两个参数我调了很久,300和30的组合在大部分设备上表现都不错。数值太大会显得生硬,太小又容易抖动。
这里有个小插曲,我一开始把transition写在variants里面,结果发现动画效果不对。折腾了半天发现,当需要覆盖默认动画时,transition应该放在组件属性里,而不是variants定义中。
踩坑提醒:这三点一定注意
分享几个血泪教训:
- 别一股脑全用layout特性,按需使用”position”或”size”能省不少性能开销
- 记得给父容器设置will-change: transform,这个对GPU加速很有帮助
- 复杂的连续动画最好拆分成多个独立动画,避免过度渲染
说个尴尬的事,最开始我还怀疑是服务器响应慢导致的卡顿,特意优化了接口性能。fetch(‘https://jztheme.com/api/data’)改成并发请求后才发现跟动画卡顿完全没关系,白忙活了一天。
还有点小瑕疵,但无伤大雅
虽然主要问题解决了,但现在快速滑动时偶尔还是会有一点点掉帧。不过已经比之前强太多了,至少在主流机型上都能流畅运行。
另外我发现当列表项超过50个时,初次渲染还是会有点卡顿。暂时的解决方案是做懒加载,分批渲染数据:
const [visibleItems, setVisibleItems] = useState(10);
useEffect(() => {
const handleScroll = () => {
if (window.scrollY + window.innerHeight >= document.body.offsetHeight - 100) {
setVisibleItems((prev) => prev + 10);
}
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
// 在渲染时
{items.slice(0, visibleItems).map(...)}
以上是我踩坑后的总结
总的来说,Framer Motion确实是个很强大的动画库,但在移动端使用时还是要多注意性能问题。特别是layout特性和transition配置,这两个是最容易出问题的地方。
这个方案不是最优的,但胜在改动小效果明显。如果你有更好的实现方式欢迎评论区交流。后续我打算研究下怎么结合React.memo进一步优化性能,到时候再跟大家分享。

暂无评论