如何在ReactJS与Framer Motion中从头重复执行动画序列
解决Framer Motion动画序列无限循环问题
问题根源
你当前的实现逻辑存在问题:三个await animation.start()执行完成后,最后调用的animation.start()仅设置了过渡参数但未指定任何动画属性,相当于没有触发任何动画;此外,Framer Motion的repeat参数仅作用于单个动画或keyframes定义的序列,无法直接复用之前异步串联的多个动画步骤。
方案1:用Keyframes定义完整序列(推荐)
将整个动画流程用keyframes整合,直接通过transition配置实现无限循环,这是Framer Motion设计序列动画的标准方式:
import { motion, useAnimation } from "framer-motion"; import { useEffect, useRef } from "react"; function YourComponent() { const line_1 = useRef(null); const _line = useRef(null); const line_2 = useRef(null); const animation = useAnimation(); useEffect(() => { // 确保元素挂载后获取尺寸 const line1Width = line_1.current?.getBoundingClientRect().width || 0; const lineHeight = _line.current?.getBoundingClientRect().height || 0; const line2Width = line_2.current?.getBoundingClientRect().width || 0; animation.start({ keyframes: [ { x: line1Width }, { y: -lineHeight / 2 }, { x: line2Width * 2 } ], transition: { duration: 0.5, // 可根据需求调整每个关键帧的动画时长 repeat: Infinity, repeatDelay: 1, repeatType: "loop" } }); }, []); return ( <> {/* 你的参考元素 */} <div ref={line_1} className="line-1">Line 1</div> <div ref={_line} className="_line">Line</div> <div ref={line_2} className="line-2">Line 2</div> <motion.div className="w-3 h-3 bg-red-600" animate={animation} /> </> ); }
方案2:递归重复执行序列函数
如果需要保留异步步骤的写法,可以在序列执行完毕后,等待延迟再递归调用自身,实现循环:
import { motion, useAnimation } from "framer-motion"; import { useEffect, useRef } from "react"; function YourComponent() { const line_1 = useRef(null); const _line = useRef(null); const line_2 = useRef(null); const animation = useAnimation(); async function sequence() { const line1Width = line_1.current?.getBoundingClientRect().width || 0; const lineHeight = _line.current?.getBoundingClientRect().height || 0; const line2Width = line_2.current?.getBoundingClientRect().width || 0; await animation.start({ x: line1Width }); await animation.start({ y: -lineHeight / 2 }); await animation.start({ x: line2Width * 2 }); // 等待1秒后重复序列 await new Promise(resolve => setTimeout(resolve, 1000)); sequence(); } useEffect(() => { sequence(); }, []); return ( <> <div ref={line_1} className="line-1">Line 1</div> <div ref={_line} className="_line">Line</div> <div ref={line_2} className="line-2">Line 2</div> <motion.div className="w-3 h-3 bg-red-600" animate={animation} /> </> ); }
注意事项
- 方案1更符合Framer Motion的设计理念,动画控制更统一,也更容易维护。
- 方案2适合需要在动画步骤中插入额外逻辑的场景,递归调用不会造成内存泄漏(组件卸载时会自动终止)。
- 必须确保获取元素尺寸时,元素已经挂载到DOM中,否则会得到0值,可以通过调整useEffect依赖或确保元素提前渲染来避免。
内容的提问来源于stack exchange,提问作者Mohamed Majilan
相关产品推荐
相关产品推荐

