Files
novalon-website/src/hooks/use-scroll-reveal.ts
T
张翔 fecbfd1990 feat: 添加预览效果页面并优化交互效果
refactor: 优化代码健壮性和类型安全

style: 更新字体样式和全局CSS

fix: 修复IntersectionObserver潜在空引用问题

chore: 更新依赖和ESLint配置

build: 更新构建ID和路由配置
2026-02-24 10:24:05 +08:00

95 lines
2.4 KiB
TypeScript

import { useEffect, useRef, useState } from 'react';
interface UseScrollRevealOptions {
threshold?: number;
rootMargin?: string;
triggerOnce?: boolean;
}
export function useScrollReveal({
threshold = 0.1,
rootMargin = '0px',
triggerOnce = true,
}: UseScrollRevealOptions = {}) {
const ref = useRef<HTMLElement>(null);
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
const element = ref.current;
if (!element) {return;}
const observer = new IntersectionObserver(
([entry]) => {
if (entry?.isIntersecting) {
setIsVisible(true);
if (triggerOnce) {
observer.unobserve(element);
}
} else if (!triggerOnce) {
setIsVisible(false);
}
},
{ threshold, rootMargin }
);
observer.observe(element);
return () => observer.disconnect();
}, [threshold, rootMargin, triggerOnce]);
return { ref, isVisible };
}
interface UseScrollProgressOptions {
threshold?: number;
}
export function useScrollProgress({ threshold: _threshold = 0 }: UseScrollProgressOptions = {}) {
const [progress, setProgress] = useState(0);
useEffect(() => {
const handleScroll = () => {
const scrollTop = window.pageYOffset;
const docHeight = document.documentElement.scrollHeight - window.innerHeight;
const scrollProgress = scrollTop / docHeight;
setProgress(Math.min(1, Math.max(0, scrollProgress)));
};
window.addEventListener('scroll', handleScroll, { passive: true });
handleScroll();
return () => window.removeEventListener('scroll', handleScroll);
}, []);
return progress;
}
interface UseParallaxOptions {
speed?: number;
}
export function useParallax({ speed = 0.5 }: UseParallaxOptions = {}) {
const ref = useRef<HTMLElement>(null);
const [offset, setOffset] = useState(0);
useEffect(() => {
const handleScroll = () => {
if (!ref.current) {return;}
const rect = ref.current.getBoundingClientRect();
const scrolled = window.pageYOffset;
const elementTop = rect.top + scrolled;
const parallaxOffset = (scrolled - elementTop) * speed;
setOffset(parallaxOffset);
};
window.addEventListener('scroll', handleScroll, { passive: true });
handleScroll();
return () => window.removeEventListener('scroll', handleScroll);
}, [speed]);
return { ref, offset };
}