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

React组件展开动画瞬现、收起动画正常,原因是什么?

问题:React展开/收起组件动画异常:展开瞬间完成,收起正常收缩

开发了一个React展开收起组件,点击切换状态时,收起动画能正常缓慢收缩到18px高度,但展开动画几乎瞬间完成,怀疑对动画原理理解有误。

代码展示

React组件代码

import React, { useState } from 'react';
import './style.css';

const text = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.";

export default function App() {
  const [collapsed, setCollapsed] = useState(true);

  function handleOnClick() {
    setCollapsed(!collapsed);
  }

  return (
    <div
      className={collapsed ? 'collapsed' : 'expanded'}
      onClick={handleOnClick}
    >
      {text}
    </div>
  );
}

CSS代码

.collapsed {
  max-height: 18px;
  transition: max-height 0.3s ease;
  overflow: hidden;
}

.expanded {
  max-height: 500px;
  transition: max-height 0.3s ease;
}

问题原因

核心问题出在**max-height过渡的计算逻辑**:

  • 收起动画:从文本实际高度(远小于500px)过渡到18px,这个高度变化占了500px-18px区间的大部分比例,浏览器会完整使用你设置的0.3s过渡时长,动画表现正常。
  • 展开动画:从18px过渡到500px,但文本实际高度远低于500px,浏览器会按实际需要的高度变化量与总区间的比例分配过渡时间。比如实际文本高度是200px,从18px到200px的变化仅占总区间的约37%,对应过渡时长只有0.11秒左右,视觉上就像瞬间完成。

解决方案

方案1:设置贴近实际内容的max-height

估算文本的实际高度,将.expanded的max-height设为接近该值的数值,让展开时的高度变化区间与实际需求匹配:

.expanded {
  max-height: 200px; /* 替换为文本实际高度的近似值 */
  transition: max-height 0.3s ease;
}

方案2:用JS获取精确高度(精准适配)

通过scrollHeight获取文本容器的真实高度,动态设置展开状态的max-height:

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

const text = "Lorem Ipsum is simply dummy text..."; // 原文本内容

export default function App() {
  const [collapsed, setCollapsed] = useState(true);
  const contentRef = useRef(null);
  const [contentHeight, setContentHeight] = useState(0);

  // 组件挂载后获取真实高度
  useEffect(() => {
    if (contentRef.current) {
      setContentHeight(contentRef.current.scrollHeight);
    }
  }, []);

  function handleOnClick() {
    setCollapsed(!collapsed);
  }

  return (
    <div
      ref={contentRef}
      className={collapsed ? 'collapsed' : 'expanded'}
      style={!collapsed ? { maxHeight: `${contentHeight}px` } : {}}
      onClick={handleOnClick}
    >
      {text}
    </div>
  );
}

对应的CSS可以简化:

.collapsed {
  max-height: 18px;
  transition: max-height 0.3s ease;
  overflow: hidden;
}

.expanded {
  transition: max-height 0.3s ease;
}

方案3:使用CSS Grid实现真实高度过渡(现代浏览器推荐)

利用CSS Grid的grid-template-rows特性,直接基于内容真实高度做过渡,无需依赖max-height:

.collapsed {
  display: grid;
  grid-template-rows: 18px;
  transition: grid-template-rows 0.3s ease;
  overflow: hidden;
}

.expanded {
  grid-template-rows: auto;
}

注意:该方案需要浏览器支持CSS Grid,兼容Chrome、Firefox、Edge等现代浏览器,动画效果最自然。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 14:15:42