React中展开Div时如何实现下方元素平滑下移?
解决团队成员简介展开时下方元素平滑下移的问题
问题场景
我正在开发网站的「Team」板块,现有布局如下:
- 一个包含4张水平排列卡片的容器,每张卡片包含成员图片、姓名、职位和「Read Bio」按钮
- 点击按钮后,需要在对应卡片下方展开一个显示成员完整简介的容器
- 目标效果:简介容器从上至下平滑展开,同时推动下方的「Advisory Board」区域自然下移
- 当前问题:「Advisory Board」区域是跳变下移,而非平滑过渡,使用React + Framer Motion开发
核心原因与解决方案
当前问题源于简介容器用了绝对定位,脱离了文档流,导致下方元素无法感知其高度变化。要实现平滑推动效果,需让简介容器回到正常文档流中,通过Framer Motion的高度动画控制展开/收起:
具体实现步骤
- 移除绝对定位:删除简介容器的
top定位样式,让它处于正常文档流,高度变化会直接带动下方元素移动 - 定义动画变体:设置
menuVars,控制容器的height、opacity和overflow属性,实现从上到下的展开过渡 - 处理进出动画:用
AnimatePresence包裹简介容器,确保展开和收起都有平滑动画 - 简化点击逻辑:点击卡片按钮时,只需切换
activeBio状态,无需计算位置
完整代码示例
import { motion, AnimatePresence } from "framer-motion"; import { useState } from "react"; import styles from "./Team.module.css"; // 定义动画变体 const menuVars = { initial: { height: 0, opacity: 0, overflow: "hidden" }, animate: { height: "auto", opacity: 1, overflow: "visible", transition: { duration: 0.5, ease: "easeInOut" } }, exit: { height: 0, opacity: 0, overflow: "hidden", transition: { duration: 0.5, ease: "easeInOut" } } }; const Team = () => { const [activeBio, setActiveBio] = useState(null); // 示例成员数据 const founders = [ { id: 1, name: "张三", position: "CEO", bio: "张三的完整简介,包含个人经历、职责范围等详细内容..." }, { id: 2, name: "李四", position: "CTO", bio: "李四的完整简介,专注技术研发方向,主导过多个核心项目..." }, { id: 3, name: "王五", position: "COO", bio: "王五的完整简介,负责公司运营管理,搭建高效团队体系..." }, { id: 4, name: "赵六", position: "CFO", bio: "赵六的完整简介,把控公司财务战略,优化资金配置..." } ]; return ( <div className={styles.cardWrapper}> <div className={styles.cardsContainer}> {founders.map((member) => ( <div key={member.id} className={styles.card} onClick={() => setActiveBio(activeBio === member.id ? null : member.id)} > <img src={member.avatar || "/default-avatar.png"} alt={member.name} className={styles.cardImg} /> <h3>{member.name}</h3> <p className={styles.cardPosition}>{member.position}</p> <button className={styles.bioBtn}>Read Bio</button> </div> ))} </div> <AnimatePresence> {activeBio && ( <motion.div key={activeBio} variants={menuVars} initial="initial" animate="animate" exit="exit" className={styles.bioContainer} > <h4>{founders.find(m => m.id === activeBio)?.name}</h4> <p>{founders.find(m => m.id === activeBio)?.bio}</p> </motion.div> )} </AnimatePresence> <div className={styles.sabInfoDiv}> <div className={styles.heading}>Advisory Board</div> <p className={styles.sabInfo}> 这里是顾问团队的介绍内容,包含团队职责、成员背景等信息。 </p> </div> </div> ); }; export default Team;
配套样式(Team.module.css)
.cardWrapper { width: 100%; max-width: 1200px; margin: 0 auto; padding: 2rem; } .cardsContainer { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.5rem; margin-bottom: 1.5rem; } .card { text-align: center; padding: 1.2rem; border: 1px solid #e5e7eb; border-radius: 8px; transition: border-color 0.2s; } .card:hover { border-color: #3b82f6; } .cardImg { width: 80px; height: 80px; border-radius: 50%; margin: 0 auto 1rem; object-fit: cover; } .cardPosition { color: #6b7280; font-size: 0.9rem; margin: 0.5rem 0; } .bioBtn { background-color: #3b82f6; color: white; border: none; padding: 0.5rem 1rem; border-radius: 4px; cursor: pointer; margin-top: 1rem; } .bioContainer { padding: 1.5rem; background-color: #f9fafb; border-radius: 8px; margin-bottom: 1.5rem; } .sabInfoDiv { padding-top: 1.5rem; border-top: 1px solid #e5e7eb; } .heading { font-size: 1.2rem; font-weight: 600; margin-bottom: 0.8rem; } .sabInfo { color: #4b5563; line-height: 1.6; }
内容的提问来源于stack exchange,提问作者user24889088
相关产品推荐
相关产品推荐

