fix: 修复导航栏点击后 active 状态被滚动覆盖的问题

- 添加 isScrollingRef 标记,在点击导航后阻止滚动监听器更新 activeSection
- 改进 section 检测逻辑,使用 sectionTop 和 sectionBottom 精确判断
- 点击导航后 1 秒内不响应滚动检测,确保平滑滚动完成
This commit is contained in:
张翔
2026-02-23 08:26:03 +08:00
parent 4f9cfffdad
commit d6e87dfafe
+27 -5
View File
@@ -1,6 +1,6 @@
'use client';
import { useState, useEffect, useCallback } from 'react';
import { useState, useEffect, useCallback, useRef } from 'react';
import { Menu, X } from 'lucide-react';
import { motion, AnimatePresence } from 'framer-motion';
import { Button } from '@/components/ui/button';
@@ -10,6 +10,8 @@ export function Header() {
const [isOpen, setIsOpen] = useState(false);
const [activeSection, setActiveSection] = useState('home');
const [isScrolled, setIsScrolled] = useState(false);
const isScrollingRef = useRef(false);
const scrollTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const handleNavClick = useCallback((e: React.MouseEvent<HTMLAnchorElement>, href: string) => {
e.preventDefault();
@@ -17,6 +19,12 @@ export function Header() {
const element = document.getElementById(targetId);
if (element) {
isScrollingRef.current = true;
if (scrollTimeoutRef.current) {
clearTimeout(scrollTimeoutRef.current);
}
const headerOffset = 64;
const elementPosition = element.getBoundingClientRect().top;
const offsetPosition = elementPosition + window.pageYOffset - headerOffset;
@@ -28,6 +36,10 @@ export function Header() {
setActiveSection(targetId);
setIsOpen(false);
scrollTimeoutRef.current = setTimeout(() => {
isScrollingRef.current = false;
}, 1000);
}
}, []);
@@ -35,6 +47,10 @@ export function Header() {
const handleScroll = () => {
setIsScrolled(window.scrollY > 20);
if (isScrollingRef.current) {
return;
}
const sections = NAVIGATION.map(item => item.href.replace('#', ''));
const scrollPosition = window.scrollY + 100;
@@ -42,7 +58,8 @@ export function Header() {
const section = document.getElementById(sections[i]);
if (section) {
const sectionTop = section.offsetTop;
if (scrollPosition >= sectionTop) {
const sectionBottom = sectionTop + section.offsetHeight;
if (scrollPosition >= sectionTop && scrollPosition < sectionBottom) {
setActiveSection(sections[i]);
break;
}
@@ -53,7 +70,12 @@ export function Header() {
window.addEventListener('scroll', handleScroll, { passive: true });
handleScroll();
return () => window.removeEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
if (scrollTimeoutRef.current) {
clearTimeout(scrollTimeoutRef.current);
}
};
}, []);
return (
@@ -113,7 +135,7 @@ export function Header() {
<Button
variant="ghost"
size="sm"
onClick={(e) => {
onClick={() => {
const element = document.getElementById('contact');
if (element) {
element.scrollIntoView({ behavior: 'smooth' });
@@ -124,7 +146,7 @@ export function Header() {
</Button>
<Button
size="sm"
onClick={(e) => {
onClick={() => {
const element = document.getElementById('about');
if (element) {
element.scrollIntoView({ behavior: 'smooth' });