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

Framer Motion子元素移除时Stagger动画未触发问题求助

Framer Motion列表元素退出动画失效问题解决方案

我有一个包含多个Box.tsx子项的ParentBox.tsx列表,点击Add按钮添加新Box时动画正常,但以下两种场景动画失效:

  • 点击Box移除该项时,Framer Motion直接移除元素无退出动画;
  • 点击Remove All清空列表时,无退出stagger效果。

期望实现:单个元素移除时的退出动画,清空列表时元素逐个退出的stagger动画。


现有代码

Parent Box代码

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

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"
    >
      <button
        onClick={() => {
          id++;
          setItems([...items, { id: id, text: `Click to delete id ${id}` }]);
        }}
      >
        Add
      </button>
      <button
        onClick={() => {
          id++;
          setItems([]);
        }}
      >
        Remove All
      </button>

      <motion.ol
        variants={variantsBoxContainer}
        initial="hidden"
        animate="show"
        exit="hidden"
      >
        <AnimatePresence mode="popLayout">
          {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, transition: { duration: 2 } },
  show: { opacity: 1, top: 0, transition: { duration: 2 } }
};
export const Box = (props: BoxProps) => {
  return (
    <motion.li
      className="box"
      variants={variantBox}
      onClick={() => {
        props.onRemove(props.data);
      }}
    >
      {props.data.text}
    </motion.li>
  );
};

已尝试方案

  • 在Box组件添加/移除initial、animate、exit属性;
  • 添加/移除when选项;
  • 尝试AnimatedPresence的所有mode;
  • 为hidden(退出)变体添加自定义延迟函数;
  • 确保所有Box拥有唯一key。

解决方案

要解决这两个问题,需要调整两个核心配置:

1. 为Box组件添加退出动画标识

修改Box组件的motion.li,添加initial、animate和exit属性,让AnimatedPresence能识别元素的退出状态并触发动画:

const variantBox: Variants = {
  hidden: { opacity: 0, top: -100, transition: { duration: 2 } },
  show: { opacity: 1, top: 0, transition: { duration: 2 } }
};
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. 调整父容器与AnimatedPresence的结构,实现Remove All的stagger效果

将AnimatedPresence包裹整个motion.ol,并给motion.ol添加key={items.length},当列表从非空变为空时,父容器会触发exit动画,其variants中的staggerChildren会控制子元素逐个退出:

修改ParentBox中的列表部分:

<AnimatePresence>
  {items.length > 0 && (
    <motion.ol
      key={items.length} // 关键:通过key变化触发父容器的退出动画
      variants={variantsBoxContainer}
      initial="hidden"
      animate="show"
      exit="hidden"
    >
      {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);
            }}
          />
        ))}
    </motion.ol>
  )}
</AnimatePresence>

补充说明

  • 单个元素删除时,AnimatedPresence检测到对应Box元素被移除,触发其exit="hidden"动画;
  • 点击Remove All时,items.length变为0,motion.ol因key变化被移除,触发其exit="hidden"变体,其中的staggerChildren: 0.1和staggerDirection: -1会让子元素从后往前逐个退出;
  • 可保留AnimatedPresence的mode="popLayout",确保元素退出后布局平滑调整。

内容的提问来源于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.11 20:40:24