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

使用framer-motion库时,变体的Transition duration不生效问题

问题:数据变化后动画无法自动恢复初始状态

期望首次数据变化时,animate状态的动画运行1秒后恢复至initial状态,但实际动画始终保持,未按预期恢复。

相关代码

const variants = {
  initial: { boxShadow: 'inset 0 0 0px 0 rgba(0,0,0,0)' },
  animate: {
    boxShadow: 'inset 0 0 20px 0 #ffc940',
    transition: {
      duration: 1,
      ease: 'easeInOut',
    },
  },
  exit: { boxShadow: 'inset 0 0 0px 0 rgba(0,0,0,0)' },
};
const FlashingTableCell: FC<FlashingTableCellProps> = ({ value, type }) => {
  const prevValueRef = useRef(value);
  const [shouldAnimate, setShouldAnimate] = useState(false);

  useEffect(() => {
    if (type !== 'button') return;
    prevValueRef.current !== value
      ? setShouldAnimate(true)
      : setShouldAnimate(false);
    prevValueRef.current = value;
  }, [value]);

  return (
    <AnimatePresence>
      <motion.td
        key={String(value)}
        variants={variants}
        initial="initial"
        animate={shouldAnimate ? 'animate' : 'initial'}
      >
        {value}
      </motion.td>
    </AnimatePresence>
  );
};

问题原因

当前代码中,shouldAnimate仅在数据变化时被设为true,但没有任何机制在动画执行完毕后将其重置为false,导致组件一直停留在animate状态。

修复方案

方案一:利用动画完成回调

通过motion组件的onAnimationComplete回调,在动画结束后自动重置状态:

const FlashingTableCell: FC<FlashingTableCellProps> = ({ value, type }) => {
  const prevValueRef = useRef(value);
  const [shouldAnimate, setShouldAnimate] = useState(false);

  useEffect(() => {
    if (type !== 'button') return;
    if (prevValueRef.current !== value) {
      setShouldAnimate(true);
    }
    prevValueRef.current = value;
  }, [value]);

  return (
    <motion.td
      key={String(value)}
      variants={variants}
      initial="initial"
      animate={shouldAnimate ? 'animate' : 'initial'}
      onAnimationComplete={() => {
        if (shouldAnimate) setShouldAnimate(false);
      }}
    >
      {value}
    </motion.td>
  );
};

方案二:使用定时器重置状态

在触发动画后,设置一个与动画时长一致的定时器,到期后重置状态(注意清理定时器避免内存泄漏):

const FlashingTableCell: FC<FlashingTableCellProps> = ({ value, type }) => {
  const prevValueRef = useRef(value);
  const [shouldAnimate, setShouldAnimate] = useState(false);

  useEffect(() => {
    if (type !== 'button') return;
    let timer: NodeJS.Timeout;
    if (prevValueRef.current !== value) {
      setShouldAnimate(true);
      timer = setTimeout(() => {
        setShouldAnimate(false);
      }, 1000); // 与动画duration保持一致
    }
    prevValueRef.current = value;
    return () => clearTimeout(timer);
  }, [value]);

  return (
    <motion.td
      key={String(value)}
      variants={variants}
      initial="initial"
      animate={shouldAnimate ? 'animate' : 'initial'}
    >
      {value}
    </motion.td>
  );
};

注:原代码中的AnimatePresence组件并未实际用到(没有触发exit动画的场景),可以直接移除,不影响功能。

内容的提问来源于stack exchange,提问作者Lonli-Lokli

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 12:45:17