Sfoglia il codice sorgente

feat: 添加滚动导航组件

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat/alipay-payment
fengsilin 1 mese fa
parent
commit
8041c22c5a
1 ha cambiato i file con 90 aggiunte e 0 eliminazioni
  1. +90
    -0
      web/src/pages/Home/components/ScrollNavigator.jsx

+ 90
- 0
web/src/pages/Home/components/ScrollNavigator.jsx Vedi File

@@ -0,0 +1,90 @@
/*
Copyright (C) 2025 QuantumNous

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.

For commercial licensing, please contact support@quantumnous.com
*/

import React, { useState, useEffect, useCallback } from 'react';

const ScrollNavigator = ({ sections }) => {
const [activeIndex, setActiveIndex] = useState(0);

const getActiveSection = useCallback(() => {
const container = document.querySelector('.home-snap-container');
if (!container) return 0;

const scrollTop = container.scrollTop;
const containerHeight = container.clientHeight;
const scrollCenter = scrollTop + containerHeight * 0.4;

let closestIndex = 0;
let closestDistance = Infinity;

sections.forEach((section, index) => {
const el = document.getElementById(section.id);
if (el) {
const rect = el.getBoundingClientRect();
const elCenter = rect.top + rect.height / 2 + scrollTop;
const distance = Math.abs(elCenter - scrollCenter);

if (distance < closestDistance) {
closestDistance = distance;
closestIndex = index;
}
}
});

return closestIndex;
}, [sections]);

useEffect(() => {
const container = document.querySelector('.home-snap-container');
if (!container) return;

const handleScroll = () => {
const newIndex = getActiveSection();
setActiveIndex(newIndex);
};

container.addEventListener('scroll', handleScroll, { passive: true });
handleScroll(); // 初始化

return () => container.removeEventListener('scroll', handleScroll);
}, [getActiveSection]);

const scrollToSection = (id) => {
const el = document.getElementById(id);
if (el) {
el.scrollIntoView({ behavior: 'smooth' });
}
};

return (
<nav className="scroll-navigator">
{sections.map((section, index) => (
<button
key={section.id}
className={`scroll-navigator-dot ${activeIndex === index ? 'active' : ''}`}
onClick={() => scrollToSection(section.id)}
title={section.label}
aria-label={section.label}
/>
))}
</nav>
);
};

export default ScrollNavigator;

Caricamento…
Annulla
Salva