NextJS项目中NextUI卡片动画失效问题求助
解决NextUI卡片过渡动画不生效的问题
问题根源
你猜的没错——直接用{expandedCard !== null ? <ExpandedCard /> : <卡片网格/>}这种条件渲染时,React会完全卸载旧组件、挂载新组件,被卸载的元素直接从DOM中消失,CSS过渡没有可作用的元素,自然不会生效。
解决方案:保留DOM元素,用状态控制样式过渡
核心思路是让卡片网格和展开卡片同时存在于DOM中,通过React状态控制它们的透明度、可见性等样式属性,让CSS过渡能正常触发。
修改后的完整代码
import Section from '@/components/section' import { Card, CardBody } from "@nextui-org/react"; import { useState } from 'react'; const Index = () => { const [expandedCard, setExpandedCard] = useState<number | null>(null); const handleCardClick = (index: number) => { setExpandedCard(index); }; const cards = [ { name: 'Tost', price: '140' }, { name: 'Sendvic', price: '150' }, { name: 'Sendvic', price: '150' }, { name: 'Sendvic', price: '150' }, { name: 'Sendvic', price: '150' }, { name: 'Sendvic', price: '150' }, { name: 'Sendvic', price: '150' }, { name: 'Sendvic', price: '150' }, ]; return ( <Page> <Section> {/* 外层容器,让展开卡片可以绝对定位覆盖网格 */} <div style={{ position: 'relative', width: '100%', minHeight: '30vh' }}> {/* 卡片网格区域 */} <div style={{ opacity: expandedCard !== null ? 0 : 1, transition: 'opacity 0.5s ease-in-out', pointerEvents: expandedCard !== null ? 'none' : 'auto', // 展开时禁用网格点击 }} > <div className="grid grid-cols-2 gap-2 justify-center"> {cards.map((card, index) => ( <Card key={index} isPressable onClick={() => handleCardClick(index)} > <CardBody> <img src="https://via.placeholder.com/200x150" alt="Placeholder Image" /> <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', }}> <h5>{card.name}</h5> <p>{card.price} RSD</p> </div> </CardBody> </Card> ))} </div> </div> {/* 展开卡片区域 */} {expandedCard !== null && ( <div style={{ position: 'absolute', top: 0, left: '50%', transform: expandedCard !== null ? 'translateX(-50%) scale(1)' : 'translateX(-50%) scale(0.9)', width: '90%', height: '30vh', backgroundColor: 'white', padding: '20px', borderRadius: '10px', boxShadow: '0px 0px 10px rgba(0, 0, 0, 0.2)', opacity: expandedCard !== null ? 1 : 0, transition: 'all 0.5s ease-in-out', zIndex: 10, display: 'flex', flexDirection: 'column', justifyContent: 'space-between' }} > <div> <h2>{cards[expandedCard].name}</h2> <p>{cards[expandedCard].price} RSD</p> </div> <button onClick={() => setExpandedCard(null)}>Close</button> </div> )} </div> </Section> </Page> ); }; export default Index
关键修改点
- 统一外层容器:用
position: relative的容器包裹两个元素,让展开卡片可以绝对定位覆盖在网格上方。 - 保留DOM元素:不再用条件渲染切换组件,而是同时渲染网格和展开卡片(展开卡片用
&&控制是否存在,但仅在第一次展开时挂载,之后只是样式变化)。 - 样式控制显隐:
- 卡片网格:通过
opacity控制透明度,展开时设为0,并添加pointerEvents: none防止误点击。 - 展开卡片:初始
opacity: 0+scale(0.9),展开时切换为opacity: 1+scale(1),配合transition: all 0.5s实现淡入+缩放的过渡效果。
- 卡片网格:通过
- 修复原代码bug:原内部组件
ExpandedCard无法直接调用setExpandedCard,改为直接在父组件渲染展开卡片,避免props传递问题。
进阶优化(可选)
- 如果想让卡片网格的消失动画更自然,可以添加
transform: translateY(20px)的过渡,让网格向下淡出。 - 可以用NextUI的
Modal组件替代自定义展开卡片,Modal自带过渡动画,更符合组件库的设计规范。
内容的提问来源于stack exchange,提问作者Pitic
相关产品推荐
相关产品推荐

