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

如何为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 15:10:44