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

如何让CSS挂载动画在组件卸载时反向执行?

原生JS/CSS实现React组件卸载时淡出动画的解决方案

我明白你遇到的问题——React组件卸载时会直接移除DOM元素,导致淡出动画还没来得及执行就消失了。你用动画库解决是很省心的选择,但原生方案的底层逻辑其实也挺清晰的,核心思路就是先触发动画,等动画完全结束后再真正卸载组件,下面给你详细拆解实现步骤:

1. 定义CSS动画关键帧

先写好淡入和淡出的基础动画样式,注意用forwards让动画结束后保持最终状态:

@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes fadeOut {
  from { opacity: 1; }
  to { opacity: 0; }
}

.fade-in {
  animation: fadeIn 0.5s forwards;
}

.fade-out {
  animation: fadeOut 0.5s forwards;
}

2. React组件中控制动画与卸载时序

我们需要拆分状态,用一个状态控制动画触发,另一个状态控制组件是否渲染,再通过监听animationend事件来衔接动画和卸载:

import { useState, useEffect, useRef } from 'react';

function AnimatedBox() {
  // 控制组件是否渲染的核心状态
  const [isRendered, setIsRendered] = useState(true);
  // 控制是否触发淡出动画
  const [isFadingOut, setIsFadingOut] = useState(false);
  const boxRef = useRef(null);

  // 监听动画结束事件,动画完成后再卸载组件
  useEffect(() => {
    const handleAnimationEnd = () => {
      if (isFadingOut) {
        setIsRendered(false);
        setIsFadingOut(false);
      }
    };

    const box = boxRef.current;
    if (box) {
      box.addEventListener('animationend', handleAnimationEnd);
    }

    // 清理监听事件
    return () => {
      if (box) {
        box.removeEventListener('animationend', handleAnimationEnd);
      }
    };
  }, [isFadingOut]);

  // 触发淡出动画的方法
  const handleClose = () => {
    setIsFadingOut(true);
  };

  // 组件未渲染时返回null
  if (!isRendered) return null;

  return (
    <div
      ref={boxRef}
      className={isFadingOut ? 'fade-out' : 'fade-in'}
      style={{ width: '100px', height: '100px', backgroundColor: '#42b983' }}
    >
      <button onClick={handleClose}>关闭方块</button>
    </div>
  );
}

3. 核心原理说明

  • 点击关闭时,不是直接把isRendered设为false,而是先开启isFadingOut状态,给方块加上淡出动画类;
  • 当浏览器触发animationend事件(动画执行完毕),再把isRendered设为false,此时组件才会真正从DOM中卸载;
  • 用useRef绑定DOM元素,确保能正确监听动画事件,避免React状态更新导致的元素引用丢失。

额外注意点

  • 不要用setTimeout代替animationend监听,因为不同浏览器的动画执行精度有差异,事件监听是最可靠的方式;
  • 如果是列表中的动态组件(比如循环渲染的项),只需要给每个项单独维护isFadingOut和isRendered状态即可,逻辑完全一致;
  • 你提到的animation: both其实是控制动画的填充模式,但它解决不了React卸载DOM的时序问题,所以必须通过状态拆分来衔接。

你用动画库(比如Framer Motion、React Transition Group)确实能省不少事,它们内部已经封装了这套动画+DOM卸载的时序逻辑,但原生方案能让你更清楚底层的运作方式~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 03:44:07