You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 12:12:28