如何为React+TailwindCSS项目的汉堡菜单添加过渡动画?
为React+TailwindCSS的汉堡菜单添加过渡动画
我使用Vite、React与TailwindCSS开发项目,现有如下Navbar组件及App.jsx代码,希望为汉堡菜单添加平滑的展开/收起过渡动画,但尝试后未成功,请问该如何实现?
我的Navbar组件
import { useState } from 'react'; import { navlinks } from '../constants/constants'; import hmclose from '../assets/icons/hamburger_close.png'; import hm from '../assets/icons/hamburger.png'; const Navbar = () => { const [toggle, setToggle] = useState(false); return ( <div className={`flex flex-col md:flex-row w-full mt-[24px] transition-height ${toggle ? 'h-auto' : 'h-[33px]'}`}> <nav className='flex items-center justify-center w-full'> <ul className='list-none md:flex hidden gap-[48px]'> {navlinks.map((navitem, index) => ( <li key={index} className={`cursor-pointer text-[16px] text-accent`}> <a href="">{navitem.text}</a> </li> ))} </ul> </nav> <div className='flex px-[16px] gap-[16px]'> <button className="hidden md:flex bg-secondary text-primary py-[8px] px-[8px] rounded-[8px] outline-none items-center"> Contact </button> <button className="hidden md:flex border-2 border-secondary text-accent bg-transparent py-[8px] px-[8px] rounded-[8px] outline-none items-center"> Resume </button> </div> <div className='md:hidden flex px-[16px] gap-[16px]'> <img src={toggle ? hmclose : hm} alt="menu" className='w-[48px] h-[48px] object-contain' onClick={() => setToggle((prev) => !prev)} /> </div> {toggle && ( <div className='md:hidden flex flex-col w-full items-center justify-center'> <ul className='list-none text-center'> {navlinks.map((navitem, index) => ( <li key={index} className={`cursor-pointer text-[16px] text-accent mb-4`} > <a href={`#${navitem.link}`}>{navitem.text}</a> </li> ))} </ul> </div> )} </div> ); }; export default Navbar;
我的App.jsx文件
import React from 'react' import Navbar from './components/Navbar' const App = () => { return ( <div className="bg-primary w-full overflow-hidden"> <div> <Navbar /> </div> </div> ) } export default App
解决方案
问题根源
- Tailwind的
transition-height对h-auto无效,CSS无法过渡到自动高度 - 直接用
{toggle && ...}控制菜单挂载/卸载,没有过渡时间,元素会直接出现/消失
修改步骤
- 用
max-height代替height实现过渡:设置收起时max-h-0,展开时max-h-[500px](值要足够容纳菜单内容) - 让移动端菜单始终存在,通过
max-height和overflow-hidden控制显示隐藏 - 给菜单容器添加过渡类,确保动画平滑
修改后的Navbar组件
import { useState } from 'react'; import { navlinks } from '../constants/constants'; import hmclose from '../assets/icons/hamburger_close.png'; import hm from '../assets/icons/hamburger.png'; const Navbar = () => { const [toggle, setToggle] = useState(false); return ( <div className="flex flex-col md:flex-row w-full mt-[24px]"> {/* 桌面端导航栏 */} <nav className='flex items-center justify-center w-full'> <ul className='list-none md:flex hidden gap-[48px]'> {navlinks.map((navitem, index) => ( <li key={index} className={`cursor-pointer text-[16px] text-accent`}> <a href="">{navitem.text}</a> </li> ))} </ul> </nav> {/* 桌面端按钮 */} <div className='flex px-[16px] gap-[16px]'> <button className="hidden md:flex bg-secondary text-primary py-[8px] px-[8px] rounded-[8px] outline-none items-center"> Contact </button> <button className="hidden md:flex border-2 border-secondary text-accent bg-transparent py-[8px] px-[8px] rounded-[8px] outline-none items-center"> Resume </button> </div> {/* 移动端汉堡按钮 */} <div className='md:hidden flex px-[16px] gap-[16px]'> <img src={toggle ? hmclose : hm} alt="menu" className='w-[48px] h-[48px] object-contain' onClick={() => setToggle((prev) => !prev)} /> </div> {/* 移动端菜单(始终存在,通过max-height控制显示) */} <div className={`md:hidden flex flex-col w-full items-center justify-center overflow-hidden transition-all duration-300 ease-in-out ${toggle ? 'max-h-[500px]' : 'max-h-0'}`}> <ul className='list-none text-center py-4'> {navlinks.map((navitem, index) => ( <li key={index} className={`cursor-pointer text-[16px] text-accent mb-4`} > <a href={`#${navitem.link}`}>{navitem.text}</a> </li> ))} </ul> </div> </div> ); }; export default Navbar;
关键修改说明
- 移除了原容器的
transition-height和动态height设置,max-height过渡更可靠 - 移动端菜单不再用条件渲染,而是通过
max-h-0(收起)和max-h-[500px](展开)控制,配合transition-all duration-300实现平滑过渡 - 给菜单ul添加了
py-4,避免收起时出现多余间距 - 过渡动画的时长和缓动函数可以通过
duration-*和ease-*类调整,比如duration-500改为500毫秒过渡
内容的提问来源于stack exchange,提问作者Lokesh Bhortake
相关产品推荐
相关产品推荐

