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

如何为条件渲染的Tailwind CSS React模态框添加指定动画

解决React+Tailwind模态框条件渲染导致动画不生效的问题

问题根源

直接通过{isModalOpen && <Modal />}的条件渲染方式,会让模态框组件被立即挂载/卸载,没有留给Tailwind过渡动画执行的时间,因此动画无法正常触发。

解决方案

放弃直接条件卸载组件的逻辑,改用状态控制模态框的可见性类名,等退出动画完全结束后再真正卸载组件。具体修改分为两步:

1. 改造Modal组件,支持动画状态控制

新增isOpen属性控制显示/隐藏的类名,同时添加onAnimationEnd回调通知父组件动画完成:

import { useEffect, useRef } from 'react';

export const Modal = ({ isOpen, onClose, onAnimationEnd, children, title = "" }) => {
  const handleBackdropClick = (e) => {
    e.target === e.currentTarget && onClose();
  };

  const modalRef = useRef(null);

  // 控制body滚动状态
  useEffect(() => {
    document.body.style.overflow = isOpen ? "hidden" : "unset";
  }, [isOpen]);

  // 监听动画结束事件,通知父组件卸载
  useEffect(() => {
    const handleEnd = () => {
      if (!isOpen) onAnimationEnd();
    };
    const element = modalRef.current;
    if (element) {
      element.addEventListener('animationend', handleEnd);
      return () => element.removeEventListener('animationend', handleEnd);
    }
  }, [isOpen, onAnimationEnd]);

  return (
    <div
      ref={modalRef}
      className={`fixed inset-0 flex justify-center items-top py-2 px-2 z-50 transition-all ease-in-out delay-150 duration-300 ${
        isOpen ? 'opacity-100 visible' : 'opacity-0 invisible pointer-events-none'
      }`}
      onClick={handleBackdropClick}
    >
      {/* 背景遮罩层 */}
      <div className="absolute inset-0 bg-slate-950 bg-opacity-50 transition-opacity ease-in-out delay-150 duration-300"></div>
      {/* 模态框主体 */}
      <div className={`relative bg-white border-2 p-4 md:p-6 shadow-xl rounded-3xl w-full md:w-min overflow-y-auto min-h-fit h-max transform transition-all ease-in-out delay-150 duration-300 ${
        isOpen ? 'scale-100 opacity-100' : 'scale-90 opacity-0'
      }`}>
        <div className="flex items-center justify-between sticky top-0 gap-8 bg-white pb-2">
          <h4 className="text-primary-text text-base md:text-xl font-bold w-max">
            {title}
          </h4>
          <span className="cursor-pointer text-gray-500" onClick={onClose}>
            <interfaceIcons.CloseIcon size="24" />
          </span>
        </div>
        {children}
      </div>
    </div>
  );
};

2. 修改HomePage的调用逻辑

新增两个状态:一个控制组件是否挂载,一个控制动画的显示/隐藏,确保动画有足够的执行时间:

import { useState } from 'react';
import { Modal } from './Modal';

export const HomePage= () => {
  const [isModalVisible, setIsModalVisible] = useState(false);
  const [isModalMounted, setIsModalMounted] = useState(false);

  // 打开模态框:先挂载组件,再触发显示动画
  const handleModalOpen = () => {
    setIsModalMounted(true);
    setTimeout(() => setIsModalVisible(true), 10); // 给DOM渲染留极短时间,避免动画失效
  };

  // 关闭模态框:先触发退出动画
  const handleModalClose = () => {
    setIsModalVisible(false);
  };

  // 动画结束后卸载组件
  const handleAnimationEnd = () => {
    setIsModalMounted(false);
  };

  return (
    <>
      <button onClick={handleModalOpen}>Open modal</button>

      {isModalMounted && (
        <Modal 
          isOpen={isModalVisible}
          title="My modal" 
          onClose={handleModalClose}
          onAnimationEnd={handleAnimationEnd}
        >
          content here!
        </Modal>
      )}
    </>
  );
};

核心细节说明

  • 用isModalMounted控制组件是否存在于DOM中,isModalVisible控制动画类名切换,保证动画有完整的执行周期。
  • 背景层和模态框主体都添加了ease-in-out delay-150 duration-300类,实现同步的过渡效果。
  • 关闭时先切换类名触发退出动画,动画结束后再通过回调卸载组件;打开时先挂载组件,延迟极短时间再触发显示动画,避免DOM未渲染完成导致动画不生效。

内容的提问来源于stack exchange,提问作者Muhammed Sahad

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 19:12:44