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

React组件内容变更时的顺序动画实现问题求助

实现React多行内容的顺序切换动画

核心思路

通过状态跟踪动画阶段 + CSS过渡严格控制三个动画的执行顺序:淡出旧内容 → 过渡容器高度 → 淡入新内容。利用transitionend事件监听每个动画的结束时机,触发下一阶段的动画逻辑,避免定时器带来的时序偏差。

自定义实现代码示例

以下是基于函数组件的完整实现,每行可直接替换为你的独立组件:

import { useState, useEffect, useRef } from 'react';
import './ContentSwitcher.css';

const CONTENT_ROW_HEIGHT = 40; // 固定行高,根据实际需求调整

export default function ContentSwitcher({ content }) {
  const [currentContent, setCurrentContent] = useState(content);
  const [nextContent, setNextContent] = useState(null);
  const [animationPhase, setAnimationPhase] = useState('idle'); // idle | fadingOut | resizing | fadingIn
  const [containerHeight, setContainerHeight] = useState(content.length * CONTENT_ROW_HEIGHT);
  const containerRef = useRef(null);

  // 监听内容变更,启动动画序列
  useEffect(() => {
    if (content === currentContent) return;
    setNextContent(content);
    setAnimationPhase('fadingOut');
  }, [content, currentContent]);

  // 监听淡出动画结束,触发高度过渡
  useEffect(() => {
    if (animationPhase !== 'fadingOut') return;
    
    const handleTransitionEnd = (e) => {
      if (e.propertyName !== 'opacity') return;
      const newHeight = nextContent.length * CONTENT_ROW_HEIGHT;
      setContainerHeight(newHeight);
      setAnimationPhase('resizing');
    };

    const rows = containerRef.current.querySelectorAll('.content-row');
    rows.forEach(row => row.addEventListener('transitionend', handleTransitionEnd));
    return () => rows.forEach(row => row.removeEventListener('transitionend', handleTransitionEnd));
  }, [animationPhase, nextContent]);

  // 监听高度过渡结束,切换内容并触发淡入
  useEffect(() => {
    if (animationPhase !== 'resizing') return;
    
    const handleTransitionEnd = (e) => {
      if (e.propertyName !== 'height') return;
      setCurrentContent(nextContent);
      setNextContent(null);
      setAnimationPhase('fadingIn');
    };

    containerRef.current.addEventListener('transitionend', handleTransitionEnd);
    return () => containerRef.current.removeEventListener('transitionend', handleTransitionEnd);
  }, [animationPhase, nextContent]);

  // 监听淡入动画结束,回到空闲状态
  useEffect(() => {
    if (animationPhase !== 'fadingIn') return;
    
    const handleTransitionEnd = (e) => {
      if (e.propertyName !== 'opacity') return;
      setAnimationPhase('idle');
    };

    const rows = containerRef.current.querySelectorAll('.content-row');
    rows.forEach(row => row.addEventListener('transitionend', handleTransitionEnd));
    return () => rows.forEach(row => row.removeEventListener('transitionend', handleTransitionEnd));
  }, [animationPhase]);

  return (
    <div
      ref={containerRef}
      className="content-container"
      style={{ height: `${containerHeight}px` }}
    >
      {(animationPhase === 'idle' || animationPhase === 'fadingIn') && (
        currentContent.map((item, index) => (
          <div
            key={index}
            className={`content-row ${animationPhase === 'fadingIn' ? 'fade-in' : ''}`}
          >
            {item} {/* 替换为你的独立行组件 */}
          </div>
        ))
      )}
      {animationPhase === 'fadingOut' && (
        currentContent.map((item, index) => (
          <div
            key={index}
            className="content-row fade-out"
          >
            {item}
          </div>
        ))
      )}
    </div>
  );
}

对应的CSS文件ContentSwitcher.css:

.content-container {
  overflow: hidden;
  transition: height 1s ease-in-out;
}

.content-row {
  height: 40px; /* 与JS中定义的固定行高保持一致 */
  opacity: 1;
  transition: opacity 1s ease-in-out;
}

.content-row.fade-out {
  opacity: 0;
}

.content-row.fade-in {
  opacity: 0;
  animation: fadeIn 1s ease-in-out forwards;
}

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

代码说明

  1. 状态管控:通过animationPhase精准控制每个动画步骤,确保严格按顺序执行。
  2. 事件监听:用transitionend替代定时器,保证动画完成后再触发下一步,避免视觉错乱。
  3. 高度计算:基于固定行高×行数动态计算容器高度,确保高度过渡平滑自然。
  4. 内容切换:在高度过渡完成后再替换内容,避免新旧内容同时显示的问题。

现成组件替代方案

如果不想手动实现,可以尝试扩展react-transition-group的序列动画能力,但自定义实现更灵活,能完全匹配你的三步动画需求,无需额外依赖。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 21:32:33