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

如何在关闭Panel组件时实现入场动画的反向效果?

解决React Spring Panel关闭时无反向动画的问题

要实现关闭时的反向退场动画,核心是让动画完成后再执行组件卸载,同时根据显示状态动态切换动画目标。以下是具体方案:

关键改动点

  • 新增isOpen props,用于控制Panel的显示状态(由父组件传入,不再直接卸载组件)
  • 根据isOpen动态设置useSpring的目标值,关闭时触发反向动画
  • 监听动画结束事件,待退场动画完成后再调用onClose通知父组件更新状态

修改后的完整代码

import { useSpring, animated } from 'react-spring';
import { CgClose } from 'react-icons/cg';
import clsx from 'clsx';

type PanelProps = {
  children: React.ReactNode;
  onClose: () => void;
  classNames?: {
    panelSize?: string;
  };
  isOpen: boolean; // 新增控制显示的props
};

const Panel: React.FC<PanelProps> = ({ children, onClose, classNames, isOpen }) => {
  const transitions = useSpring({
    transform: isOpen ? "translateX(0)" : "translateX(100%)",
    from: { transform: "translateX(100%)" },
    config: { duration: 200 },
    onRest: () => {
      // 动画结束后,若处于关闭状态则调用onClose
      if (!isOpen) {
        onClose();
      }
    },
  });

  // 动画完成且已关闭时,才销毁组件
  if (!isOpen && !transitions.transform.isAnimating) {
    return null;
  }

  return (
    <div className='z-10 lg:fixed lg:left-0 lg:top-0 lg:flex lg:h-screen lg:w-screen'>
      {/* 遮罩层添加渐隐动画优化体验 */}
      <animated.div 
        onClick={() => !transitions.transform.isAnimating && onClose()}
        className='w-full bg-black'
        style={{ opacity: isOpen ? 0.4 : 0, transition: 'opacity 200ms' }}
      />

      <animated.div
        className={clsx(
          "fixed right-0 top-0 z-10 h-screen w-full overflow-y-auto bg-white shadow-lg",
          classNames?.panelSize ?? "w-full md:w-4/12"
        )}
        style={transitions}>
        <button
          onClick={() => !transitions.transform.isAnimating && onClose()}
          className='absolute right-6 top-6 rounded-full bg-white p-1 duration-100 ease-in hover:scale-125 '>
          <CgClose size={20} />
        </button>

        <div>{children}</div>
      </animated.div>
    </div>
  );
};

export default Panel;

父组件配合示例

父组件需要维护isOpen状态,通过状态更新控制面板显示/隐藏,而非直接卸载组件:

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

const ParentComponent = () => {
  const [isPanelOpen, setIsPanelOpen] = useState(false);

  return (
    <div>
      <button onClick={() => setIsPanelOpen(true)}>打开面板</button>
      <Panel 
        isOpen={isPanelOpen} 
        onClose={() => setIsPanelOpen(false)}
      >
        {/* 面板内容 */}
        <p>这是右侧面板的内容</p>
      </Panel>
    </div>
  );
};

export default ParentComponent;

额外说明

  • 点击关闭按钮和遮罩层时,先判断动画是否正在执行,避免重复触发导致的异常
  • 遮罩层的渐隐动画是可选优化,让整体交互更流畅
  • 通过onRest回调确保动画完全结束后才通知父组件,保证退场动画完整播放

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 05:36:24