本文介绍如何通过 css opacity 过渡与状态协调控制,实现卡片列表与单张选中卡片之间的平滑淡入淡出效果,避免 dom 完全替换导致的动画中断问题。
本文介绍如何通过 css opacity 过渡与状态协调控制,实现卡片列表与单张选中卡片之间的平滑淡入淡出效果,避免 dom 完全替换导致的动画中断问题。
在 React 中实现卡片选择态的视觉过渡,关键在于保持 DOM 结构稳定,而非条件渲染({condition ? <A /> : <B />})导致元素频繁挂载/卸载——这会使 CSS transition 失效,因为浏览器无法对“不存在的元素”执行渐变动画。
将所有卡片(列表模式)和选中卡片(详情模式)保留在同一 DOM 容器中,通过 opacity 和 pointer-events 配合 transition 控制可见性与交互,而非 display: none 或条件移除节点。
const App = () => { const [selectedServiceIndex, setSelectedServiceIndex] = useState(null); const [isServiceSelected, setIsServiceSelected] = useState(false); const [isTransitioning, setIsTransitioning] = useState(false); // 控制过渡阶段 const handleServiceClick = (index) => { if (index < 0 || index >= services_data.length) return; if (isServiceSelected && selectedServiceIndex === index) { // 取消选中:先淡出,再隐藏 setIsTransitioning(true); setTimeout(() => { setIsServiceSelected(false); setIsTransitioning(false); }, 300); } else { // 切换选中:先淡出当前视图,再更新索引并淡入 setIsTransitioning(true); setTimeout(() => { setSelectedServiceIndex(index); setIsServiceSelected(true); setIsTransitioning(false); }, 300); } }; const services = services_data.map((item, index) => ( <div key={index} onClick={() => handleServiceClick(index)}> <Service name={item.name} imageName={item.imageName} className={item.className} desc={item.desc} /> </div> )); return ( <main> {/* 始终存在 —— 列表视图(默认显示,选中时淡出) */} <section className="section services-section" id="services" style={{ opacity: isServiceSelected && !isTransitioning ? 0 : 1, pointerEvents: isServiceSelected && !isTransitioning ? 'none' : 'auto', transition: 'opacity 0.3s ease, pointer-events 0.3s step-end' }} > <div className="container"> <div className="row"> <div className="page-header"> <div className="section-title"> <h2>Services</h2> <p>Enhance efficiency, reliability, and quality with automated test solutions.</p> </div> </div> </div> <div className="row services-row">{services}</div> </div> </section> {/* 始终存在 —— 详情视图(默认隐藏,选中时淡入) */} <div className="selected-service" style={{ opacity: isServiceSelected && !isTransitioning ? 1 : 0, pointerEvents: isServiceSelected && !isTransitioning ? 'auto' : 'none', transition: 'opacity 0.3s ease, pointer-events 0.3s step-end', // 可选:添加 transform 提升性能 willChange: 'opacity' }} > {isServiceSelected && selectedServiceIndex !== null && services[selectedServiceIndex]} </div> </main> );};
该方案兼顾简洁性、可维护性与用户体验——既保留了语义清晰的 DOM 结构,又实现了专业级的视觉反馈,是 React 卡片切换场景下的稳健实践。