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
相关产品推荐
相关产品推荐

