React可展开侧边菜单动画问题:独立控制与图标切换修复
React可折叠侧边菜单修复方案
问题根源
- 全局状态联动:用单个
open变量控制所有菜单,点击任意一个会触发所有菜单同步展开/收起 - 图标定位错误:
.nav_icons用绝对定位,但父元素.sidebarMenu未设置position: relative,所有图标会相对于页面定位,堆在同一位置 - 无平滑动画:
display: none/block不支持CSS过渡,无法实现平滑展开/收起效果
修复后的React代码
import { useState } from 'react'; // 请确保已正确导入图标组件和资源 import Home from './icons/Home'; import GroupAdd from './icons/GroupAdd'; import PostAdd from './icons/PostAdd'; import collapse_icon from './icons/collapse.png'; import expand_icon from './icons/expand.png'; function Sidebar() { // 用对象存储每个菜单的独立展开状态 const [menuStates, setMenuStates] = useState({ userCreation: true, mainReport: true }); // 通用切换函数,仅修改对应菜单的状态 const toggleMenu = (menuKey) => { setMenuStates(prev => ({ ...prev, [menuKey]: !prev[menuKey] })); }; return ( <div className="sidebar"> <div className="sidebarWrapper"> <div className="sidebarMenu"> {/* 统一标题和图标点击区域 */} <div className="sidebarTitleContainer" onClick={() => toggleMenu('userCreation')}> <h3 className="sidebarTitle">User Creation</h3> <img src={menuStates.userCreation ? collapse_icon : expand_icon} alt="Expand" className="nav_icons" /> </div> <ul className={`sidebarList ${menuStates.userCreation ? 'show_menu' : 'hide_menu'}`} > <li className="sidebarListItem active"> <Home className="sidebarIcon" /> <span className="titleText">Home</span> </li> <li className="sidebarListItem"> <GroupAdd className="sidebarIcon" /> <span className="titleText"> New users</span> </li> <li className="sidebarListItem"> <PostAdd className="sidebarIcon" /> <span className="titleText">Home</span> </li> </ul> </div> <div className="sidebarMenu"> <div className="sidebarTitleContainer" onClick={() => toggleMenu('mainReport')}> <h3 className="sidebarTitle">Main Report</h3> <img src={menuStates.mainReport ? collapse_icon : expand_icon} alt="Expand" className="nav_icons" /> </div> <ul className={`sidebarList ${menuStates.mainReport ? 'show_menu' : 'hide_menu'}`} > <li className="sidebarListItem active"> <Home className="sidebarIcon" /> <span className="titleText">Sales Report</span> </li> <li className="sidebarListItem"> <GroupAdd className="sidebarIcon" /> <span className="titleText">Orders Report</span> </li> <li className="sidebarListItem"> <PostAdd className="sidebarIcon" /> <span className="titleText">Stock Report</span> </li> </ul> </div> </div> </div> ); } export default Sidebar;
修复后的CSS代码
.sidebar { flex: 1; height: calc(100vh - 50px); background-color: rgb(251, 251, 255); position: sticky; top: 50px; overflow-y: scroll; } .sidebarWrapper { padding: 10px; color: #555; } .sidebarMenu { margin-bottom: 10px; cursor: pointer; /* 关键:添加相对定位,让图标相对于当前菜单容器定位 */ position: relative; } /* 新增标题容器,统一点击区域 */ .sidebarTitleContainer { display: flex; align-items: center; justify-content: center; position: relative; } .sidebarTitle { font-size: 15px; color: white; background-color: #646464; padding: 4px; border-top-left-radius: 8px; border-top-right-radius: 8px; width: 100%; text-align: center; margin: 0; } .titleText { color: #990000; font-size: 14px; } .sidebarList { list-style: none; padding: 5px; margin: 0; /* 用max-height实现平滑过渡,设置足够大的默认值 */ max-height: 500px; opacity: 1; transition: max-height 0.3s ease, opacity 0.3s ease; overflow: hidden; } .sidebarListItem { padding: 5px; cursor: pointer; display: flex; align-items: center; border-radius: 10px; } .sidebarListItem.active, .sidebarListItem:hover { background-color: rgb(240, 240, 255); } .sidebarIcon { margin-right: 5px; font-size: 20px !important; } /* 保留原媒体查询逻辑 */ @media only screen and (max-width: 500px) { .sidebarListItem { display: grid; } .titleText { font-size: 11px; } .sidebarIcon { margin-right: 1px; font-size: 12px !important; } .sidebarTitle { font-size: 12px; } } @media only screen and (min-width: 501px) and (max-width: 700px) { .sidebarListItem { display: grid; } .titleText { font-size: 13px; } .sidebarIcon { margin-right: 1px; font-size: 17px !important; } .sidebarTitle { font-size: 12px; } } @media only screen and (min-width: 701px) and (max-width: 992px) { .sidebarListItem { display: grid; } .titleText { font-size: 13px; } .sidebarIcon { margin-right: 1px; font-size: 19px !important; } .sidebarTitle { font-size: 14px; } } .nav_icons { width: 15px; height: 15px; background-color: #fafafa; filter: invert(76%) sepia(30%) saturate(3461%) hue-rotate(321deg) brightness(98%) contrast(91%); position: absolute; top: 50%; right: 15px; /* 垂直居中图标 */ transform: translateY(-50%); } /* 替换display为max-height+opacity实现平滑过渡 */ .hide_menu { max-height: 0; opacity: 0; padding: 0 5px; } .show_menu { max-height: 500px; opacity: 1; padding: 5px; }
关键修改说明
- 状态独立管理:改用对象存储每个菜单的展开状态,每个菜单仅触发自身的切换逻辑
- 图标定位修复:给
.sidebarMenu添加position: relative,并调整图标为垂直居中,避免重叠 - 平滑动画实现:用
max-height和opacity替代display属性,支持CSS过渡动画 - 点击区域优化:新增
.sidebarTitleContainer,让标题和图标区域均可触发菜单切换,提升交互体验
内容的提问来源于stack exchange,提问作者Denis Morgan
相关产品推荐
相关产品推荐

