48 lines
1.0 KiB
TypeScript
48 lines
1.0 KiB
TypeScript
import { useEffect, useRef, useState } from "react";
|
|
import { useMediaQuery } from "@/hooks/useMediaQuery";
|
|
|
|
interface CardAnimationConfig {
|
|
duration?: number;
|
|
delay?: number;
|
|
stagger?: number;
|
|
}
|
|
|
|
export const useCardAnimation = (
|
|
config?: CardAnimationConfig
|
|
) => {
|
|
const { duration = 0.6, delay = 0, stagger = 0.1 } = config || {};
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
const itemRefs = useRef<(HTMLDivElement | null)[]>([]);
|
|
const isMobile = useMediaQuery("(max-width: 768px)");
|
|
const [isAnimating, setIsAnimating] = useState(false);
|
|
|
|
useEffect(() => {
|
|
if (!containerRef.current) return;
|
|
|
|
const observer = new IntersectionObserver(
|
|
([entry]) => {
|
|
if (entry.isIntersecting && !isAnimating) {
|
|
setIsAnimating(true);
|
|
}
|
|
},
|
|
{ threshold: 0.1 }
|
|
);
|
|
|
|
observer.observe(containerRef.current);
|
|
|
|
return () => {
|
|
observer.disconnect();
|
|
};
|
|
}, [isAnimating]);
|
|
|
|
return {
|
|
containerRef,
|
|
itemRefs,
|
|
isMobile,
|
|
isAnimating,
|
|
duration,
|
|
delay,
|
|
stagger,
|
|
};
|
|
};
|