Next.js自定义Accordion向上收起正常但向下展开无动画问题
问题分析与修复方案
你的问题核心在于**display: none直接切换会打断CSS过渡动画**:展开时你直接将元素从display: none改为block,浏览器不会计算transform的过渡过程,而是直接渲染最终状态;而收起时你用setTimeout延迟设置display: none,动画已经完成,所以能正常生效。
修复步骤
1. 调整JS逻辑,确保展开时先渲染初始状态再触发动画
移除手动控制display的冗余代码,改用requestAnimationFrame让浏览器先渲染元素的初始隐藏状态,再执行展开动画;收起时等待动画完成后再隐藏元素。
修改后的JS代码:
import { useState, useRef } from "react"; import styles from "./AsideCheckout.module.css"; const AsideCheckout = (props) => { const [isExpanded, setIsExpanded] = useState(false); const asideRef = useRef(null); const toggleAccordion = () => { if (!isExpanded) { // 展开:先让元素可见,等待浏览器渲染初始transform状态 asideRef.current.style.display = "block"; requestAnimationFrame(() => { setIsExpanded(true); }); } else { // 收起:先触发动画,完成后再隐藏元素 setIsExpanded(false); setTimeout(() => { asideRef.current.style.display = "none"; }, 700); // 时长和CSS过渡时间保持一致 } }; return ( <> <div className={styles.aside_accordion_button} onClick={toggleAccordion}> <button>CLICK</button> </div> <aside ref={asideRef} className={`${styles.aside_checkout} ${isExpanded ? styles.aside_show_checkout : ""}`} style={{ display: isExpanded ? "block" : undefined }} > ... aside content etc </aside> </> ); }; export default AsideCheckout;
2. 简化CSS,保留核心过渡逻辑
将初始隐藏的display: none写在默认类里,无需手动在JS初始化:
.aside_checkout { grid-column: 1; grid-row: -2; overflow-y: hidden; transform: translateY(-100%); transition: transform 0.7s ease-in-out; display: none; /* 初始隐藏,不占用页面空间 */ } .aside_show_checkout { transform: translateY(0); transition: transform 0.7s ease-out; } .aside_accordion_button { grid-row: -3; display: flex; /* 其他自定义样式 */ }
修复原理
- 展开时:先设置
display: block让元素进入DOM流,此时元素处于transform: translateY(-100%)的隐藏状态;通过requestAnimationFrame等待浏览器完成这一帧渲染后,再添加展开类,浏览器会自动计算从-100%到0的过渡动画。 - 收起时:先移除展开类触发收起动画,等待动画完成后再设置
display: none,既保证动画流畅,又能让元素收起后不占用空间,下方内容自动上移。
内容的提问来源于stack exchange,提问作者Mohamed
相关产品推荐
相关产品推荐

