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

React Framer Motion列表子项移除时Stagger动画失效求助

问题:Framer Motion中motion.li退出动画不生效

问题描述

实现了motion.ol和motion.li的显示/隐藏效果,但点击删除时motion.li的退出动画无法正常触发。相关代码如下:

父组件(ParentBox)代码

const variantsBoxContainer: Variants = {
  hidden: {
    opacity: 0,
    transition: {
      staggerChildren: 0.1,
      delayChildren: 0.3
    }
  },
  show: {
    opacity: 1,
    transition: {
      staggerChildren: 0.1,
      delayChildren: 0.3
    }
  }
};

let id = 3;
export const ParentBox = (props: ParentBoxProps) => {
  const [items, setItems] = useState<Item[]>([
    { id: 1, text: "Test #1" },
    { id: 2, text: "Test #2" }
  ]);
  return (
    <motion.div
      className="parentbox"
      // style={{ transform: `translateX(${props.showBox ? 0 : 100}px)` }}
    >
      <button
        onClick={() => {
          id++;
          setItems([...items, { id: id, text: `Click to delete id ${id}` }]);
        }}
      >
        Add
      </button>

      <motion.ol
        variants={variantsBoxContainer}
        initial="hidden"
        animate="show"
        exit="hidden"
      >
        <AnimatePresence>
          {items
            .sort((a, b) => a.id - b.id)
            .map((d) => (
              <Box
                key={d.id}
                data={d}
                onRemove={(item) => {
                  const newList = items.filter((i) => i.id !== item.id);
                  console.log(newList);
                  setItems(newList);
                }}
              />
            ))}
        </AnimatePresence>
      </motion.ol>
    </motion.div>
  );
};

子组件(Box)代码

const variantBox: Variants = {
  hidden: { opacity: 0, top: -100 },
  show: { opacity: 1, top: 0 }
};
export const Box = (props: BoxProps) => {
  return (
    <motion.li
      className="box"
      variants={variantBox}
      onClick={() => {
        props.onRemove(props.data);
      }}
    >
      {props.data.text}
    </motion.li>
  );
};

解决方法

退出动画失效的核心原因有两个:子组件未配置退出动画触发逻辑,以及父容器动画干扰了子元素的退出时机。具体修改步骤如下:

1. 配置子组件的退出动画

为motion.li添加exit属性,并完善变体的退出过渡配置:

const variantBox: Variants = {
  hidden: { opacity: 0, top: -100 },
  show: { opacity: 1, top: 0 },
  exit: { 
    opacity: 0, 
    top: 100, 
    transition: { duration: 0.3 } 
  }
};
export const Box = (props: BoxProps) => {
  return (
    <motion.li
      className="box"
      variants={variantBox}
      initial="hidden"
      animate="show"
      exit="hidden" // 关键:添加exit属性触发退出动画
      onClick={() => {
        props.onRemove(props.data);
      }}
    >
      {props.data.text}
    </motion.li>
  );
};

2. 调整父组件的容器配置

移除motion.ol上的exit="hidden"属性,避免父容器动画覆盖子元素的退出逻辑:

// 父组件中的motion.ol修改为:
<motion.ol
  variants={variantsBoxContainer}
  initial="hidden"
  animate="show"
  // 移除exit="hidden",防止干扰子元素退出动画
>
  <AnimatePresence>
    {items
      .sort((a, b) => a.id - b.id)
      .map((d) => (
        <Box
          key={d.id}
          data={d}
          onRemove={(item) => {
            const newList = items.filter((i) => i.id !== item.id);
            setItems(newList);
          }}
        />
      ))}
  </AnimatePresence>
</motion.ol>

3. 可选:实现有序退出效果

如果需要子元素退出时按顺序播放动画,可给AnimatePresence添加配置:

<AnimatePresence 
  exitBeforeEnter
  initial={false}
>
  {/* 子元素映射代码 */}
</AnimatePresence>

关键原理

  • AnimatePresence必须直接包裹动态增删的元素,且元素需有唯一稳定的key(此处用d.id符合要求)。
  • 动态元素需显式设置exit属性,关联对应动画变体,Framer Motion才会在元素被移除前播放退出动画。
  • 父容器的exit动画会优先执行,导致子元素还未播放退出动画就被隐藏,因此需要移除父容器的exit配置,或确保父容器过渡时长包含子元素的退出动画时长。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 09:45:41