Image and text animation
diwali
This commit is contained in:
63
src/hooks/useInView.tsx
Normal file
63
src/hooks/useInView.tsx
Normal file
@ -0,0 +1,63 @@
|
||||
import { useState, useEffect, RefObject } from 'react';
|
||||
|
||||
interface UseInViewOptions {
|
||||
threshold?: number | number[];
|
||||
triggerOnce?: boolean;
|
||||
}
|
||||
|
||||
const useInView = (
|
||||
ref: RefObject<Element>,
|
||||
options: UseInViewOptions = { threshold: 0.1, triggerOnce: true },
|
||||
): boolean => {
|
||||
const { threshold = 0.1, triggerOnce = true } = options;
|
||||
const [isVisible, setIsVisible] = useState<boolean>(false);
|
||||
|
||||
// Validate threshold
|
||||
const isValidThreshold =
|
||||
typeof threshold === 'number'
|
||||
? threshold >= 0 && threshold <= 1
|
||||
: Array.isArray(threshold) && threshold.every((t) => t >= 0 && t <= 1);
|
||||
|
||||
if (!isValidThreshold) {
|
||||
console.warn(
|
||||
'Invalid threshold value passed to useInView. It should be between 0 and 1.',
|
||||
);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const element = ref.current;
|
||||
if (!element) return;
|
||||
|
||||
const prefersReducedMotion = window.matchMedia(
|
||||
'(prefers-reduced-motion: reduce)',
|
||||
);
|
||||
if (prefersReducedMotion.matches) {
|
||||
setIsVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
setIsVisible(true);
|
||||
if (triggerOnce) {
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
{ threshold },
|
||||
);
|
||||
|
||||
observer.observe(element);
|
||||
|
||||
return () => {
|
||||
if (element) observer.unobserve(element);
|
||||
};
|
||||
}, [ref, threshold, triggerOnce]);
|
||||
|
||||
return isVisible;
|
||||
};
|
||||
|
||||
export default useInView;
|
||||
Reference in New Issue
Block a user