如何实现折叠导航栏先收缩后内容滚动的交互效果?
实现分步收缩导航栏的方案
原生HTML/CSS/JavaScript 实现
核心思路是分两个阶段处理滚动事件:
- 第一阶段:滚动距离未超过导航栏「初始高度-最小高度」的差值时,仅收缩导航栏高度,同时同步调整内容的上边距,让内容始终紧贴导航栏底部
- 第二阶段:滚动距离超过该差值后,导航栏固定为最小高度,内容正常随滚动向上移动
代码示例
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>分步收缩导航栏</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } .navbar { position: fixed; top: 0; left: 0; width: 100%; background: #2c3e50; color: white; transition: height 0.3s ease; height: 80px; /* 初始高度 */ min-height: 40px; /* 最小高度 */ display: flex; align-items: center; padding: 0 20px; } .content { padding: 20px; margin-top: 80px; /* 初始与导航栏高度匹配 */ transition: margin-top 0.3s ease; background: #ecf0f1; min-height: 2000px; /* 保证页面可滚动 */ } </style> </head> <body> <nav class="navbar">导航栏</nav> <div class="content"> 页面内容<br> 滚动试试效果<br> 更多内容... </div> <script> const navbar = document.querySelector('.navbar'); const content = document.querySelector('.content'); const initialNavHeight = 80; const minNavHeight = 40; const heightDiff = initialNavHeight - minNavHeight; window.addEventListener('scroll', () => { const scrollTop = window.scrollY; if (scrollTop <= heightDiff) { // 第一阶段:导航栏收缩,内容同步上移 const newNavHeight = initialNavHeight - scrollTop; navbar.style.height = `${newNavHeight}px`; content.style.marginTop = `${newNavHeight}px`; } else { // 第二阶段:导航栏固定最小高度,内容正常滚动 navbar.style.height = `${minNavHeight}px`; content.style.marginTop = `${minNavHeight}px`; } }); </script> </body> </html>
关键逻辑说明
- 计算导航栏高度差值
heightDiff作为滚动阶段的阈值 - 第一阶段通过滚动距离线性调整导航栏高度和内容上边距,确保内容始终紧贴导航栏
- 第二阶段固定导航栏和内容的间距,让内容随滚动正常上移
React 实现方案
逻辑和原生一致,通过状态管理导航栏高度和内容间距,监听滚动事件更新状态
代码示例
import { useState, useEffect } from 'react'; const App = () => { const initialNavHeight = 80; const minNavHeight = 40; const heightDiff = initialNavHeight - minNavHeight; const [navHeight, setNavHeight] = useState(initialNavHeight); const [contentMarginTop, setContentMarginTop] = useState(initialNavHeight); useEffect(() => { const handleScroll = () => { const scrollTop = window.scrollY; if (scrollTop <= heightDiff) { const newHeight = initialNavHeight - scrollTop; setNavHeight(newHeight); setContentMarginTop(newHeight); } else { setNavHeight(minNavHeight); setContentMarginTop(minNavHeight); } }; window.addEventListener('scroll', handleScroll); return () => window.removeEventListener('scroll', handleScroll); }, []); return ( <div className="app"> <nav style={{ position: 'fixed', top: 0, left: 0, width: '100%', backgroundColor: '#2c3e50', color: 'white', transition: 'height 0.3s ease', height: `${navHeight}px`, display: 'flex', alignItems: 'center', padding: '0 20px' }} > 导航栏 </nav> <div style={{ padding: '20px', marginTop: `${contentMarginTop}px`, transition: 'margin-top 0.3s ease', backgroundColor: '#ecf0f1', minHeight: '2000px' }} > 页面内容<br /> 滚动试试效果<br /> 更多内容... </div> </div> ); }; export default App;
关键逻辑说明
- 用
useState维护导航栏高度和内容间距的状态 - 在
useEffect中绑定滚动监听,分阶段更新状态 - 组件卸载时移除监听,避免内存泄漏
这个方案完全可行,能精准实现你要求的分步滚动效果:先收缩导航栏,内容始终贴紧;导航栏到达最小高度后,内容再往上滑入导航栏下方。
内容的提问来源于stack exchange,提问作者Luke
相关产品推荐
相关产品推荐

