React模态框中Framer Motion退出动画失效问题求助
修复Framer Motion模态框关闭动画失效问题
问题背景
使用React + Framer Motion开发的模态框,打开动画正常,但关闭时直接突兀消失,完全不执行退出动画。尝试调整AnimatePresence的定位和mode参数、查阅相关问题后仍未解决,以下是组件代码、CSS样式及调用逻辑,寻求修复方案。
组件代码
import React, { ReactNode } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import styles from './StickyModal.module.scss'; interface StickyModalProps { isOpen: boolean; onClose: () => void; children: ReactNode; title: string; } const StickyModal: React.FC<StickyModalProps> = ({ isOpen, onClose, title, children, }) => { return ( <AnimatePresence mode="wait"> {isOpen && ( <motion.div className={styles.backdrop} initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} onClick={e => { if (e.target === e.currentTarget) { onClose(); } }} > <motion.div className={styles.modal} initial={{ y: '100%' }} animate={{ y: 0 }} exit={{ y: '100%' }} transition={{ type: 'spring', stiffness: 200, damping: 20 }} > <span>{title}</span> <button className={styles.closeButton} onClick={onClose}> Закрыть </button> {children} </motion.div> </motion.div> )} </AnimatePresence> ); }; export default StickyModal;
CSS样式
.backdrop { position: fixed; bottom: 0; left: 0; width: 100%; height: 100lvh; overflow-y: auto; background-color: rgba(0, 0, 0, 0.5); display: flex; justify-content: center; align-items: center; border: none; z-index: 1000; } .modal { background-color: white; padding: 50px 20px; width: 100%; max-height: 70vh; border-radius: 30px 30px 0 0; position: fixed; bottom: -10px; left: 0; //overflow: hidden; } .closeButton { margin-top: 10px; }
模态框调用代码
import { useState } from 'react'; export interface IResumeStatisticExtended { linkTitle: string | null; link: string | null; } const StatsTable = () => { const [selectedItem, setSelectedItem] = useState<IResumeStatisticExtended | null>(null); const handleAction = (index: number): void => { setSelectedItem(modifiedArrayStats[index]); }; const closeModal = (): void => { setSelectedItem(null); }; return ( <div> <StatsTableColumn array={renderColumnItems('status')} title="Подробнее" hasError={modifiedArrayStats.some(hasError)} onAction={handleAction} /> {selectedItem && ( <StickyModal isOpen={true} onClose={closeModal} title={formatDateTime(selectedItem.date_time)} > <div> <p>{selectedItem.fact}</p> </div> </StickyModal> )} </div> ); }; export default StatsTable;
问题根源
- 模态框挂载逻辑错误:父组件通过
selectedItem && <StickyModal>做条件渲染,关闭时会直接卸载整个StickyModal组件,导致内部AnimatePresence无法执行退出动画。 - 缺少唯一标识key:
AnimatePresence包裹的动态元素没有唯一key,Framer Motion无法准确追踪元素的创建/销毁状态,无法触发退出动画。
修复步骤
1. 调整模态框调用逻辑
父组件不再直接条件渲染StickyModal,改为始终渲染组件,通过isOpen属性控制显示:
// StatsTable组件返回部分修改 return ( <div> <StatsTableColumn array={renderColumnItems('status')} title="Подробнее" hasError={modifiedArrayStats.some(hasError)} onAction={handleAction} /> {/* 移除条件渲染,始终渲染StickyModal,用isOpen控制显示 */} <StickyModal isOpen={!!selectedItem} onClose={closeModal} title={selectedItem ? formatDateTime(selectedItem.date_time) : ''} > {selectedItem && ( <div> <p>{selectedItem.fact}</p> </div> )} </StickyModal> </div> );
2. 给动态元素添加唯一key
在StickyModal组件中,给AnimatePresence包裹的外层motion.div添加唯一key:
<AnimatePresence mode="wait"> {isOpen && ( <motion.div key="modal-backdrop" {/* 添加唯一key */} className={styles.backdrop} initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} onClick={e => { if (e.target === e.currentTarget) { onClose(); } }} > {/* 内部modal代码保持不变 */} </motion.div> )} </AnimatePresence>
3. 优化CSS避免定位冲突(可选)
模态框的fixed定位与y轴动画可能存在叠加干扰,调整CSS让动画更流畅:
.modal { background-color: white; padding: 50px 20px; width: 100%; max-height: 70vh; border-radius: 30px 30px 0 0; position: fixed; bottom: 0; /* 改为固定在底部 */ left: 0; }
修复原理
AnimatePresence必须直接控制条件渲染的元素,而非父组件直接卸载整个组件,这样才能捕获元素的销毁状态并执行退出动画。- 唯一
key让Framer Motion能精准区分元素的创建与销毁,确保动画触发逻辑正确。
内容的提问来源于stack exchange,提问作者teplostanski
相关产品推荐
相关产品推荐

