import { useEffect, useRef, useState } from 'react';
interface UseScrollAnimationOptions {
threshold?: number;
rootMargin?: string;
triggerOnce?: boolean;
}
export const useScrollAnimation = (options: UseScrollAnimationOptions = {}) => {
const { threshold = 0.1, rootMargin = '0px', triggerOnce = true } = options;
const [isVisible, setIsVisible] = useState(false);
const elementRef = useRef<HTMLElement | null>(null);
useEffect(() => {
const element = elementRef.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 { elementRef, isVisible };
};
export const useStaggerAnimation = (
itemCount: number,
options: UseScrollAnimationOptions = {}
) => {
const { threshold = 0.1, rootMargin = '0px', triggerOnce = true } = options;
const [visibleItems, setVisibleItems] = useState<Set<number>>(new Set());
const containerRef = useRef<HTMLElement | null>(null);
useEffect(() => {
const container = containerRef.current;
if (!container) return;
const items = container.querySelectorAll('[data-stagger-item]');
if (items.length === 0) return;
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const index = parseInt(
entry.target.getAttribute('data-stagger-item') || '0',
10
);
setVisibleItems((prev) => new Set([...prev, index]));
if (triggerOnce) {
observer.unobserve(entry.target);
}
} else if (!triggerOnce) {
const index = parseInt(
entry.target.getAttribute('data-stagger-item') || '0',
10
);
setVisibleItems((prev) => {
const next = new Set(prev);
next.delete(index);
return next;
});
}
});
},
{
threshold,
rootMargin,
}
);
items.forEach((item) => observer.observe(item));
return () => {
observer.disconnect();
};
}, [itemCount, threshold, rootMargin, triggerOnce]);
return { containerRef, visibleItems };
};