You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 20:10:28