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

如何在Framer Motion中实现无卡顿的条件式动画循环?

问题原因

你的代码出现卡顿和无限循环的核心问题是:idleAnimation函数在定义时捕获了isOpen的初始值(false),React状态更新是异步的,且函数内部的变量不会同步更新状态变化。当你点击元素设置isOpen=true后,函数里的isOpen依然是旧的false值,导致while循环无法终止,持续执行动画和console.log,占用主线程造成页面冻结。

解决方案

推荐使用Framer Motion内置的动画序列和重复配置,这是更高效且符合框架设计的实现方式,无需手动维护while循环:

import { useState } from 'react';
import { motion } from 'framer-motion';

const YourComponent = () => {
  const [isOpen, setIsOpen] = useState(false);

  // 定义完整的空闲动画序列
  const idleVariants = {
    idle: [
      {
        scaleY: [1, 0.75, 1.1],
        transition: {
          duration: 0.5,
          ease: "easeIn",
          delay: 1
        }
      },
      {
        y: [0, -75, 0],
        transition: {
          ease: "easeInOut",
          duration: 1,
          y: { duration: 0.85 }
        }
      },
      {
        scaleY: [1.1, 0.95, 1],
        transition: {
          duration: 0.5,
          ease: "easeOut"
        }
      }
    ]
  };

  return (
    <motion.div
      // 根据isOpen状态控制动画播放
      animate={isOpen ? "none" : "idle"}
      variants={idleVariants}
      // 设置动画无限重复
      transition={{ repeat: Infinity }}
      style={{ originY: "100%" }}
      onClick={() => setIsOpen(true)}
      className="Gift"
    >
      {/* 组件内容 */}
    </motion.div>
  );
};

export default YourComponent;

如果坚持使用useAnimation手动控制,需要用useRef跟踪最新的isOpen状态,避免闭包捕获旧值:

import { useState, useEffect, useRef } from 'react';
import { motion, useAnimation } from 'framer-motion';

const YourComponent = () => {
  const [isOpen, setIsOpen] = useState(false);
  const controls = useAnimation();
  // 用ref同步最新的isOpen状态
  const isOpenRef = useRef(isOpen);

  useEffect(() => {
    isOpenRef.current = isOpen;
  }, [isOpen]);

  const idleAnimation = async () => {
    // 通过ref的current判断状态,确保拿到最新值
    while (!isOpenRef.current) {
      await controls.start({
        scaleY: [1, 0.75, 1.1],
        transition: { duration: 0.5, ease: "easeIn", delay: 1 }
      });
      await controls.start({
        y: [0, -75, 0],
        transition: { ease: "easeInOut", duration: 1, y: { duration: 0.85 } }
      });
      await controls.start({
        scaleY: [1.1, 0.95, 1],
        transition: { duration: 0.5, ease: "easeOut" }
      });
    }
  };

  // 组件挂载时启动动画
  useEffect(() => {
    idleAnimation();
  }, []);

  return (
    <motion.div
      animate={isOpen ? null : controls}
      style={{ originY: "100%" }}
      onClick={() => setIsOpen(true)}
      className="Gift"
    >
      {/* 组件内容 */}
    </motion.div>
  );
};

export default YourComponent;
方案对比
  • 内置序列方案:无需手动管理异步循环,Framer Motion会优化动画执行,性能更稳定,代码更简洁。
  • useAnimation+useRef方案:适合需要更精细控制动画流程的场景,但需要额外维护ref同步状态,代码复杂度稍高。

内容的提问来源于stack exchange,提问作者user22362540

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 05:37:24