React实现滚动时导航栏图标随对应组件视口切换颜色
解决方案
核心问题是你把ref绑定在了外层容器上,而非每个独立的板块组件根元素上,导致useInView无法准确判断单个板块的视口状态。以下是具体修复步骤和代码示例:
1. 状态提升与共享
将当前活跃板块的状态activeSection放到顶层父组件(比如App.js)中,让导航栏和所有板块都能访问/修改这个状态。
2. 给每个板块绑定独立的ref
每个100vh的板块组件内部,将ref绑定到自身的根DOM元素上,确保useInView监听的是单个板块的视口状态。
3. 配置useInView的合理阈值
设置threshold参数(比如0.5),确保板块大部分进入视口才触发状态更新,避免快速滚动时的频繁切换。
示例代码
顶层父组件(App.js)
import { useState } from 'react'; import Navbar from './Navbar'; import Home from './Home'; import About from './About'; import Projects from './Projects'; import Contact from './Contact'; function App() { const [activeSection, setActiveSection] = useState('home'); return ( <div className="app-container"> <Navbar activeSection={activeSection} /> <Home setActiveSection={setActiveSection} /> <About setActiveSection={setActiveSection} /> <Projects setActiveSection={setActiveSection} /> <Contact setActiveSection={setActiveSection} /> </div> ); } export default App;
单个板块组件(以Home.js为例)
其他板块(About/Projects/Contact)写法完全一致,仅需修改板块名称参数:
import { useRef, useEffect } from 'react'; import { useInView } from 'framer-motion'; function Home({ setActiveSection }) { const sectionRef = useRef(null); // 当板块50%进入视口时触发判断,且每次进入都更新状态 const isInView = useInView(sectionRef, { threshold: 0.5, once: false }); useEffect(() => { if (isInView) { setActiveSection('home'); } }, [isInView, setActiveSection]); // 确保板块占满视口 return ( <div ref={sectionRef} className="section" style={{ height: '100vh' }}> {/* 首页内容 */} </div> ); } export default Home;
导航栏组件(Navbar.js)
根据activeSection切换图标颜色:
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faHome, faUser, faFolderOpen, faEnvelope } from '@fortawesome/free-solid-svg-icons'; function Navbar({ activeSection }) { const getIconColor = (section) => { return activeSection === section ? '#ff4081' : '#888'; }; return ( <nav className="navbar" style={{ position: 'fixed', top: 20, right: 20 }}> <FontAwesomeIcon icon={faHome} style={{ color: getIconColor('home'), margin: '0 10px' }} /> <FontAwesomeIcon icon={faUser} style={{ color: getIconColor('about'), margin: '0 10px' }} /> <FontAwesomeIcon icon={faFolderOpen} style={{ color: getIconColor('projects'), margin: '0 10px' }} /> <FontAwesomeIcon icon={faEnvelope} style={{ color: getIconColor('contact'), margin: '0 10px' }} /> </nav> ); } export default Navbar;
关键说明
- 外层容器的
ref无法区分单个板块的视口状态,必须给每个板块单独绑定ref。 threshold参数可根据需求调整(0~1之间),0表示元素刚进入视口就触发,1表示完全进入才触发。- 确保每个板块的CSS设置了
height: 100vh,避免板块高度不足导致视口判断混乱。
内容的提问来源于stack exchange,提问作者oldBlueEyes
相关产品推荐
相关产品推荐

