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

手风琴组件展开时CSS transition生效,收起时无过渡效果求助

React手风琴组件收起时过渡动画失效的解决方案

问题原因

你当前的实现中,手风琴内容区域(.body)是通过{isOpen && ...}进行条件渲染的:当收起(isOpen=false)时,元素会被直接从DOM中移除,导致CSS transition没有执行时间,因此看不到收起动画;而展开时元素存在,maxHeight从0过渡到内容高度,动画正常生效。

修复方案

核心思路是始终保留内容区域的DOM节点,通过控制maxHeight属性实现展开/收起动画,而非卸载元素。

修改后的React代码

import React, { useEffect, useRef, useState } from "react";
import cx from "classnames";

import styles from "./accordion.module.scss";

export const Accordion = ({ bodyText, headerText }) => {
  const accordionRef = useRef(null);

  const [isOpen, setIsOpen] = useState(false);
  const [height, setHeight] = useState("0px");

  useEffect(() => {
    handleSetHeight();
  }, [isOpen]);

  function handleSetHeight() {
    if (accordionRef.current) {
      setHeight(isOpen ? `${accordionRef.current.scrollHeight}px` : "0px");
    }
  }

  return (
    <div
      className={cx(styles.accordion, {
        [styles["accordion--active"]]: isOpen,
      })}
    >
      <div className={styles.item}>
        <div
          className={styles.header}
          onClick={() => setIsOpen((prev) => !prev)}
        >
          <div>{headerText}</div>
          <div>{isOpen ? "-" : "+"}</div>
        </div>
        {/* 移除条件渲染,始终保留内容区域节点 */}
        <div
          className={styles.body}
          ref={accordionRef}
          style={{ maxHeight: height }}
        >
          <p>{bodyText}</p>
        </div>
      </div>
    </div>
  );
};

优化后的CSS代码(可选)

可以将过渡属性从all改为max-height,减少不必要的过渡计算,提升性能:

@import "../../colors.scss";

.accordion {
  width: 100%;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  margin-bottom: 1rem;
  border: 2px solid #afafaf;
  transition: all 350ms ease-in-out;

  &--active {
    border: 2px solid $black;
    box-shadow: 0rem 0.25rem 0.5rem #515151;
    transition: all 350ms ease-in-out;
  }

  &:last-of-type {
    margin-bottom: 0rem;
  }

  .item {
    .body {
      overflow: hidden;
      /* 明确仅过渡max-height属性 */
      transition: max-height 350ms ease-in-out;
      background-color: $white;
      padding: 0rem 1rem;
      box-sizing: border-box;
      max-height: 0px;

      p {
        margin-bottom: 1rem;
        margin-top: 0rem;
      }
    }

    .header {
      display: flex;
      justify-content: space-between;
      background-color: $white;
      font-size: 2.5rem;
      padding: 1rem;
      cursor: pointer;
    }
  }
}

修复说明

  1. 移除条件渲染后,内容区域始终存在于DOM中,收起时maxHeight从内容高度过渡到0,动画可以正常执行。
  2. 确保accordionRef始终能获取到元素节点,handleSetHeight可以正确计算并设置高度值。
  3. 可选的图标切换(+/-)提升用户交互体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 22:34:57