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

