如何为Modal添加关闭滑出动画?React/CSS实现方案
实现Modal滑出关闭动画的两种方案
方案一:CSS类切换(推荐)
首先在现有CSS基础上新增滑出动画及关闭状态类:
.modal { width: 90%; background-color: white; z-index: 30; position: fixed; top: 20vh; left: 5%; border-radius: 12px; padding: 1rem; align-items: center; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25); animation: slide-down 0.5s ease-out forwards; } /* 新增滑出动画 */ @keyframes slide-up { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(-100%); visibility: hidden; } } /* 关闭状态专属类 */ .modal--closing { animation: slide-up 0.5s ease-in forwards; }
在React组件中通过状态控制类切换,并监听动画结束事件完成Modal卸载:
import { useState, useEffect, useRef } from 'react'; const Modal = ({ children, isOpen, onClose }) => { const [isClosing, setIsClosing] = useState(false); const modalRef = useRef(null); useEffect(() => { if (!isOpen && !isClosing) { setIsClosing(true); } }, [isOpen]); useEffect(() => { const handleAnimationEnd = () => { if (isClosing) { setIsClosing(false); onClose(); } }; const modal = modalRef.current; if (modal) modal.addEventListener('animationend', handleAnimationEnd); return () => modal?.removeEventListener('animationend', handleAnimationEnd); }, [isClosing, onClose]); if (!isOpen && !isClosing) return null; return ( <div ref={modalRef} className={`${classes.modal} ${isClosing ? classes['modal--closing'] : ''}`} > {children} <button onClick={() => !isClosing && onClose()}>关闭</button> </div> ); };
方案二:React内联样式实现
通过状态直接控制内联样式中的动画属性,同样需要监听动画结束事件:
import { useState, useEffect, useRef } from 'react'; const Modal = ({ children, isOpen, onClose }) => { const [isClosing, setIsClosing] = useState(false); const [animStyle, setAnimStyle] = useState({}); const modalRef = useRef(null); useEffect(() => { if (isOpen) { setAnimStyle({ animation: 'slide-down 0.5s ease-out forwards' }); setIsClosing(false); } else if (!isClosing) { setIsClosing(true); setAnimStyle({ animation: 'slide-up 0.5s ease-in forwards' }); } }, [isOpen]); useEffect(() => { const handleAnimationEnd = () => { if (isClosing) { setIsClosing(false); onClose(); } }; const modal = modalRef.current; if (modal) modal.addEventListener('animationend', handleAnimationEnd); return () => modal?.removeEventListener('animationend', handleAnimationEnd); }, [isClosing, onClose]); if (!isOpen && !isClosing) return null; const baseStyle = { width: '90%', backgroundColor: 'white', zIndex: 30, position: 'fixed', top: '20vh', left: '5%', borderRadius: '12px', padding: '1rem', alignItems: 'center', boxShadow: '0 2px 8px rgba(0, 0, 0, 0.25)', }; return ( <div ref={modalRef} style={{ ...baseStyle, ...animStyle }} > {children} <button onClick={() => !isClosing && onClose()}>关闭</button> </div> ); };
注意:内联样式方案需要在全局CSS中提前定义两个动画:
@keyframes slide-down { from { opacity: 0; transform: translateY(-100%); } to { opacity: 1; transform: translateY(0); } } @keyframes slide-up { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(-100%); visibility: hidden; } }
内容的提问来源于stack exchange,提问作者emmy_chris
相关产品推荐
相关产品推荐

