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

React/Tailwind侧边栏动画异常:打开有动画关闭无过渡

问题分析

你的侧边栏关闭时无动画直接消失,核心原因有两点:

  1. DOM被立即卸载:使用{sidebarOpen && (...)}的条件渲染,当sidebarOpen变为false时,侧边栏DOM会被直接移除,完全没有执行关闭动画的机会。
  2. 缺少关闭动画逻辑:当前useEffect只处理了展开动画的触发,完全没有针对关闭状态的过渡逻辑。
修复方案

我们需要调整三个核心点:

  • 保留侧边栏DOM,用样式控制显隐,而非直接卸载
  • 新增关闭动画的触发与完成监听逻辑
  • 简化冗余状态与Ref,让逻辑更清晰

以下是修复后的完整代码:

import React, { useRef, useState, useEffect } from 'react';
import Sidebar from "./Sidebar";

const Menu = ({ sidebarOpen, toggleSidebar }) => {
    const sidebarElementRef = useRef(null);
    const [isClosing, setIsClosing] = useState(false);

    // 监听关闭动画完成,清理状态
    useEffect(() => {
        const handleTransitionEnd = (e) => {
            if (e.propertyName === 'transform' && isClosing) {
                setIsClosing(false);
            }
        };

        const sidebar = sidebarElementRef.current;
        if (sidebar) {
            sidebar.addEventListener('transitionend', handleTransitionEnd);
        }

        return () => {
            if (sidebar) {
                sidebar.removeEventListener('transitionend', handleTransitionEnd);
            }
        };
    }, [isClosing]);

    // 触发关闭动画
    useEffect(() => {
        if (!sidebarOpen && !isClosing) {
            setIsClosing(true);
        }
    }, [sidebarOpen]);

    // 控制侧边栏渲染时机:展开状态或正在关闭动画中
    const shouldRenderSidebar = sidebarOpen || isClosing;

    return (
        <div className="fixed bottom-0 left-0 w-full bg-white z-50 border-t-1 border-green-900">
            <div className="h-[60px] flex items-center justify-between">
                <div className="flex items-center ml-4">
                    <div
                        className="cursor-pointer p-3"
                        onClick={() => toggleSidebar(!sidebarOpen)}
                    >
                        <div className={`h-1 w-6 bg-green-700 rounded mb-1 ${sidebarOpen ? 'opacity-0' : ''}`}></div>
                        <div className={`h-1 w-6 bg-green-700 rounded mb-1 ${sidebarOpen ? 'opacity-0' : ''}`}></div>
                        <div className={`h-1 w-6 bg-green-700 rounded ${sidebarOpen ? 'opacity-0' : ''}`}></div>
                    </div>
                </div>
            </div>

            {shouldRenderSidebar && (
                <div>
                    <div 
                        className={`fixed top-0 left-0 right-0 bottom-0 z-0 bg-black bg-opacity-50 backdrop-blur-sm transition-opacity duration-500 ${!sidebarOpen ? 'opacity-0 pointer-events-none' : ''}`}
                        onClick={() => toggleSidebar(false)}
                    ></div>
                    <div 
                        ref={sidebarElementRef}
                        className="fixed top-0 left-0 bottom-0 z-10 w-[78%] md:max-w-[20rem] p-4 bg-white shadow-xl shadow-blue-gray-900/5 border-r-1 border-green-800 transition-transform duration-500 ease-in-out"
                        style={{ transform: sidebarOpen ? 'translateX(0)' : 'translateX(-100%)' }}
                    >
                        <Sidebar />
                    </div>
                </div>
            )}
        </div>
    );
}

export default Menu;
关键修复细节
  • 延迟DOM卸载:通过shouldRenderSidebar判断,当sidebarOpen变为false时,先进入isClosing状态,保留DOM直到关闭动画完成,再通过transitionend事件清理状态。
  • CSS驱动过渡:用Tailwind的transition-transform duration-500替代手动设置style,展开/关闭时通过transform属性触发动画,逻辑更简洁。
  • 遮罩层同步动画:给遮罩层添加透明度过渡,让整个关闭过程视觉更连贯。
  • 移除冗余代码:删掉了无用的sidebarContainerRef和animationComplete状态,简化组件逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 11:48:19