Shared useinview
This commit is contained in:
parent
127e024374
commit
d64da34b1e
@ -1,46 +1,13 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import { useRef } from 'react';
|
|
||||||
import Image from 'next/image';
|
|
||||||
import pralinePic from '/public/images/about-us/01-praline.jpg';
|
import pralinePic from '/public/images/about-us/01-praline.jpg';
|
||||||
import prAmPic from '/public/images/about-us/02-priyanka-amritanshu.jpg';
|
import prAmPic from '/public/images/about-us/02-priyanka-amritanshu.jpg';
|
||||||
import dryingPic from '/public/images/about-us/03-drying.jpg';
|
import dryingPic from '/public/images/about-us/03-drying.jpg';
|
||||||
import chefPic from '/public/images/about-us/04-chef.jpg';
|
import chefPic from '/public/images/about-us/04-chef.jpg';
|
||||||
import barkPic from '/public/images/about-us/05-bark.jpg';
|
import barkPic from '/public/images/about-us/05-bark.jpg';
|
||||||
import italyPic from '/public/images/about-us/06-italy.jpg';
|
import italyPic from '/public/images/about-us/06-italy.jpg';
|
||||||
import useInView from '@/hooks/useInView';
|
import { AnimatedImage } from '@/components/animated-image';
|
||||||
|
|
||||||
export default function AboutUsPage() {
|
export default function AboutUsPage() {
|
||||||
const pralinePicRef = useRef<HTMLImageElement>(null);
|
|
||||||
const isPralinePic = useInView(pralinePicRef, {
|
|
||||||
threshold: 0.1,
|
|
||||||
triggerOnce: true,
|
|
||||||
});
|
|
||||||
const prAmPicRef = useRef<HTMLImageElement>(null);
|
|
||||||
const isPrAmPic = useInView(prAmPicRef, {
|
|
||||||
threshold: 0.1,
|
|
||||||
triggerOnce: true,
|
|
||||||
});
|
|
||||||
const dryingPicRef = useRef<HTMLImageElement>(null);
|
|
||||||
const isDryingPic = useInView(dryingPicRef, {
|
|
||||||
threshold: 0.1,
|
|
||||||
triggerOnce: true,
|
|
||||||
});
|
|
||||||
const chefPicRef = useRef<HTMLImageElement>(null);
|
|
||||||
const isChefPic = useInView(chefPicRef, {
|
|
||||||
threshold: 0.1,
|
|
||||||
triggerOnce: true,
|
|
||||||
});
|
|
||||||
const barkPicRef = useRef<HTMLImageElement>(null);
|
|
||||||
const isBarkPic = useInView(barkPicRef, {
|
|
||||||
threshold: 0.1,
|
|
||||||
triggerOnce: true,
|
|
||||||
});
|
|
||||||
const italyPicRef = useRef<HTMLImageElement>(null);
|
|
||||||
const isItalyPic = useInView(italyPicRef, {
|
|
||||||
threshold: 0.1,
|
|
||||||
triggerOnce: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='overflow-x-hidden bg-white pt-28'>
|
<div className='overflow-x-hidden bg-white pt-28'>
|
||||||
<section className='flex flex-col items-center'>
|
<section className='flex flex-col items-center'>
|
||||||
@ -58,27 +25,27 @@ export default function AboutUsPage() {
|
|||||||
chocolate-making.
|
chocolate-making.
|
||||||
</div>
|
</div>
|
||||||
<div className='w-full'>
|
<div className='w-full'>
|
||||||
<Image
|
<AnimatedImage
|
||||||
ref={pralinePicRef}
|
|
||||||
src={pralinePic}
|
src={pralinePic}
|
||||||
alt='Praline'
|
alt='Praline'
|
||||||
className={`w-full h-auto zoom ${isPralinePic ? 'post' : 'pre'}`}
|
className='w-full h-auto'
|
||||||
sizes='100vw'
|
loading='lazy'
|
||||||
style={{
|
// {`w-full h-auto zoom ${isPralinePic ? 'post' : 'pre'}`}
|
||||||
width: '100%',
|
// sizes='100vw'
|
||||||
height: 'auto',
|
// style={{
|
||||||
}}
|
// width: '100%',
|
||||||
|
// height: 'auto',
|
||||||
|
// }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section className='flex flex-col md:flex-row items-center'>
|
<section className='flex flex-col md:flex-row items-center'>
|
||||||
{/* Left Column - Image */}
|
{/* Left Column - Image */}
|
||||||
<div className='w-full md:w-1/2'>
|
<div className='w-full md:w-1/2'>
|
||||||
<Image
|
<AnimatedImage
|
||||||
ref={prAmPicRef}
|
|
||||||
src={prAmPic}
|
src={prAmPic}
|
||||||
alt='Left Side Image'
|
alt='Left Side Image'
|
||||||
className={`w-full h-auto zoom ${isPrAmPic ? 'post' : 'pre'}`}
|
className='w-full h-auto'
|
||||||
sizes='100vw'
|
sizes='100vw'
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
@ -127,11 +94,10 @@ export default function AboutUsPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='w-full md:w-1/2'>
|
<div className='w-full md:w-1/2'>
|
||||||
<Image
|
<AnimatedImage
|
||||||
ref={dryingPicRef}
|
|
||||||
src={dryingPic}
|
src={dryingPic}
|
||||||
alt='Beans Drying'
|
alt='Beans Drying'
|
||||||
className={`w-full h-auto zoom ${isDryingPic ? 'post' : 'pre'}`}
|
className='w-full h-auto'
|
||||||
sizes='100vw'
|
sizes='100vw'
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
@ -143,11 +109,10 @@ export default function AboutUsPage() {
|
|||||||
{/* Fourth Three-Column Layout (Collections) */}
|
{/* Fourth Three-Column Layout (Collections) */}
|
||||||
<section className='flex flex-col md:flex-row items-center'>
|
<section className='flex flex-col md:flex-row items-center'>
|
||||||
<div className='w-full md:w-1/2'>
|
<div className='w-full md:w-1/2'>
|
||||||
<Image
|
<AnimatedImage
|
||||||
ref={chefPicRef}
|
|
||||||
src={chefPic}
|
src={chefPic}
|
||||||
alt='Beans Drying'
|
alt='Beans Drying'
|
||||||
className={`w-full h-auto zoom ${isChefPic ? 'post' : 'pre'}`}
|
className='w-full h-auto'
|
||||||
sizes='100vw'
|
sizes='100vw'
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
@ -156,11 +121,11 @@ export default function AboutUsPage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='w-full md:w-1/2'>
|
<div className='w-full md:w-1/2'>
|
||||||
<Image
|
<AnimatedImage
|
||||||
ref={barkPicRef}
|
|
||||||
src={barkPic}
|
src={barkPic}
|
||||||
alt='Beans Drying'
|
alt='Beans Drying'
|
||||||
className={`w-full h-auto zoom ${isBarkPic ? 'post' : 'pre'}`}
|
className='w-full h-auto'
|
||||||
|
loading='lazy'
|
||||||
sizes='100vw'
|
sizes='100vw'
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
@ -188,11 +153,11 @@ export default function AboutUsPage() {
|
|||||||
that define the world of chocolate making.
|
that define the world of chocolate making.
|
||||||
</div>
|
</div>
|
||||||
<div className='flex w-full'>
|
<div className='flex w-full'>
|
||||||
<Image
|
<AnimatedImage
|
||||||
ref={italyPicRef}
|
|
||||||
src={italyPic}
|
src={italyPic}
|
||||||
alt='Training'
|
alt='Training'
|
||||||
className={`w-full h-auto zoom ${isItalyPic ? 'post' : 'pre'}`}
|
className='w-full h-auto'
|
||||||
|
loading='lazy'
|
||||||
sizes='100vw'
|
sizes='100vw'
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
'use client';
|
|
||||||
import { useRef } from 'react';
|
|
||||||
import Image from 'next/image';
|
|
||||||
import brandStoryPic from '/public/images/homepage/brand-story.jpg';
|
import brandStoryPic from '/public/images/homepage/brand-story.jpg';
|
||||||
import { HomepageVideo } from '@/components/homepage-video';
|
import { HomepageVideo } from '@/components/homepage-video';
|
||||||
import { HeroSwiper } from '@/components/swiper';
|
import { HeroSwiper } from '@/components/swiper';
|
||||||
@ -9,15 +6,9 @@ import { ChocolateCategories } from '@/components/category-slider';
|
|||||||
import { InstagramFeed } from '@/components/instagram';
|
import { InstagramFeed } from '@/components/instagram';
|
||||||
import { Spotlight } from '@/components/spotlight';
|
import { Spotlight } from '@/components/spotlight';
|
||||||
import { AnimatedText } from '@/components/animated-text';
|
import { AnimatedText } from '@/components/animated-text';
|
||||||
import useInView from '@/hooks/useInView';
|
import { AnimatedImage } from '@/components/animated-image';
|
||||||
|
|
||||||
export default function HomePage() {
|
export default function HomePage() {
|
||||||
const brandStoryPicRef = useRef<HTMLImageElement>(null);
|
|
||||||
const isBrandStoryPic = useInView(brandStoryPicRef, {
|
|
||||||
threshold: 0.1,
|
|
||||||
triggerOnce: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='overflow-x-hidden'>
|
<div className='overflow-x-hidden'>
|
||||||
<HeroSwiper />
|
<HeroSwiper />
|
||||||
@ -53,16 +44,17 @@ export default function HomePage() {
|
|||||||
</div>
|
</div>
|
||||||
{/* Right Column - Image */}
|
{/* Right Column - Image */}
|
||||||
<div className='w-full md:w-1/2'>
|
<div className='w-full md:w-1/2'>
|
||||||
<Image
|
<AnimatedImage
|
||||||
ref={brandStoryPicRef}
|
// ref={brandStoryPicRef}
|
||||||
src={brandStoryPic}
|
src={brandStoryPic}
|
||||||
alt='Right Side Image'
|
alt='Right Side Image'
|
||||||
className={`w-full h-auto zoom ${isBrandStoryPic ? 'post' : 'pre'}`}
|
className='w-full h-auto'
|
||||||
sizes='100vw'
|
// {`w-full h-auto zoom ${isBrandStoryPic ? 'post' : 'pre'}`}
|
||||||
style={{
|
// sizes='100vw'
|
||||||
width: '100%',
|
// style={{
|
||||||
height: 'auto',
|
// width: '100%',
|
||||||
}}
|
// height: 'auto',
|
||||||
|
// }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
@ -1,38 +1,38 @@
|
|||||||
// src/components/AnimatedImage.tsx
|
// src/components/AnimatedImage.tsx
|
||||||
import React, { ImgHTMLAttributes, useRef } from 'react';
|
'use client';
|
||||||
|
import React from 'react';
|
||||||
import useInView from '@/hooks/useInView';
|
import useInView from '@/hooks/useInView';
|
||||||
|
import Image, { StaticImageData } from 'next/image';
|
||||||
|
|
||||||
interface AnimatedImageProps extends ImgHTMLAttributes<HTMLImageElement> {
|
interface AnimatedImageProps {
|
||||||
src: string;
|
src: string | StaticImageData;
|
||||||
alt: string;
|
alt: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
threshold?: number; // New prop
|
threshold?: number;
|
||||||
|
// Additional props if needed
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AnimatedImage: React.FC<AnimatedImageProps> = ({
|
export function AnimatedImage({
|
||||||
src,
|
src,
|
||||||
alt,
|
alt,
|
||||||
className = '',
|
className = '',
|
||||||
threshold,
|
threshold,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}: AnimatedImageProps) {
|
||||||
const ref = useRef<HTMLImageElement>(null);
|
const [ref, isVisible] = useInView({
|
||||||
const isVisible = useInView(ref, {
|
|
||||||
threshold: threshold ?? 0.1,
|
threshold: threshold ?? 0.1,
|
||||||
triggerOnce: true,
|
triggerOnce: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<img
|
<Image
|
||||||
ref={ref}
|
ref={ref}
|
||||||
src={src}
|
src={src}
|
||||||
alt={alt}
|
alt={alt}
|
||||||
className={`opacity-0 transform scale-95 transition-opacity transition-transform duration-700 ease-out ${
|
className={`zoom ${isVisible ? 'post' : 'pre'} ${className}`}
|
||||||
isVisible ? 'opacity-100 scale-100' : ''
|
|
||||||
} ${className}`}
|
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default AnimatedImage;
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// src/components/AnimatedText.tsx
|
'use client';
|
||||||
import { ReactNode, useRef } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import useInView from '@/hooks/useInView';
|
import useInView from '@/hooks/useInView';
|
||||||
|
|
||||||
interface AnimatedTextProps {
|
interface AnimatedTextProps {
|
||||||
@ -7,7 +7,7 @@ interface AnimatedTextProps {
|
|||||||
className?: string;
|
className?: string;
|
||||||
startClass?: string;
|
startClass?: string;
|
||||||
finishClass?: string;
|
finishClass?: string;
|
||||||
threshold?: number; // New prop
|
threshold?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AnimatedText({
|
export function AnimatedText({
|
||||||
@ -17,8 +17,7 @@ export function AnimatedText({
|
|||||||
finishClass,
|
finishClass,
|
||||||
threshold,
|
threshold,
|
||||||
}: AnimatedTextProps) {
|
}: AnimatedTextProps) {
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const [ref, isVisible] = useInView({
|
||||||
const isVisible = useInView(ref, {
|
|
||||||
threshold: threshold ?? 0.1,
|
threshold: threshold ?? 0.1,
|
||||||
triggerOnce: true,
|
triggerOnce: true,
|
||||||
});
|
});
|
||||||
|
80
src/hooks/shared-intersection-observer.tsx
Normal file
80
src/hooks/shared-intersection-observer.tsx
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
type Callback = (entry: IntersectionObserverEntry) => void;
|
||||||
|
|
||||||
|
class IntersectionObserverManager {
|
||||||
|
private observer: IntersectionObserver;
|
||||||
|
private callbacks: Map<Element, Callback>;
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
constructor(options: IntersectionObserverInit) {
|
||||||
|
this.callbacks = new Map();
|
||||||
|
|
||||||
|
this.observer = new IntersectionObserver(
|
||||||
|
this.handleIntersect.bind(this),
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleIntersect(entries: IntersectionObserverEntry[]) {
|
||||||
|
entries.forEach((entry) => {
|
||||||
|
const callback = this.callbacks.get(entry.target);
|
||||||
|
if (callback) {
|
||||||
|
callback(entry);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
observe(element: Element, callback: Callback) {
|
||||||
|
if (element && callback) {
|
||||||
|
this.callbacks.set(element, callback);
|
||||||
|
this.observer.observe(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unobserve(element: Element) {
|
||||||
|
if (element) {
|
||||||
|
this.callbacks.delete(element);
|
||||||
|
this.observer.unobserve(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect() {
|
||||||
|
this.observer.disconnect();
|
||||||
|
this.callbacks.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let manager: IntersectionObserverManager | null = null;
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
const getObserverManager = (options: IntersectionObserverInit) => {
|
||||||
|
if (!manager) {
|
||||||
|
manager = new IntersectionObserverManager(options);
|
||||||
|
}
|
||||||
|
return manager;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSharedIntersectionObserver = (
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
options: IntersectionObserverInit,
|
||||||
|
) => {
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
// Cleanup logic if needed
|
||||||
|
// For example, disconnect the observer when the component unmounts entirely
|
||||||
|
// This depends on your application's lifecycle
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (typeof window === 'undefined') {
|
||||||
|
// Return a dummy manager for SSR
|
||||||
|
return {
|
||||||
|
observe: () => {},
|
||||||
|
unobserve: () => {},
|
||||||
|
disconnect: () => {},
|
||||||
|
} as unknown as IntersectionObserverManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getObserverManager(options);
|
||||||
|
};
|
@ -1,63 +1,50 @@
|
|||||||
import { useState, useEffect, RefObject } from 'react';
|
'use client';
|
||||||
|
import { useState, useEffect, useRef, RefObject } from 'react';
|
||||||
|
import { useSharedIntersectionObserver } from './shared-intersection-observer';
|
||||||
|
|
||||||
interface UseInViewOptions {
|
// eslint-disable-next-line no-undef
|
||||||
threshold?: number | number[];
|
interface UseInViewOptions extends IntersectionObserverInit {
|
||||||
triggerOnce?: boolean;
|
triggerOnce?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useInView = (
|
const useInView = (
|
||||||
ref: RefObject<Element>,
|
options: UseInViewOptions,
|
||||||
options: UseInViewOptions = { threshold: 0.1, triggerOnce: true },
|
): [RefObject<HTMLImageElement>, boolean] => {
|
||||||
): boolean => {
|
const [isInView, setIsInView] = useState<boolean>(false);
|
||||||
const { threshold = 0.1, triggerOnce = true } = options;
|
const elementRef = useRef<HTMLImageElement>(null);
|
||||||
const [isVisible, setIsVisible] = useState<boolean>(false);
|
const observerManager = useSharedIntersectionObserver(options);
|
||||||
|
|
||||||
// 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(() => {
|
useEffect(() => {
|
||||||
const element = ref.current;
|
const element = elementRef.current;
|
||||||
if (!element) return;
|
if (!element) return;
|
||||||
|
|
||||||
const prefersReducedMotion = window.matchMedia(
|
const prefersReducedMotion = window.matchMedia(
|
||||||
'(prefers-reduced-motion: reduce)',
|
'(prefers-reduced-motion: reduce)',
|
||||||
);
|
);
|
||||||
if (prefersReducedMotion.matches) {
|
if (prefersReducedMotion.matches) {
|
||||||
setIsVisible(true);
|
setIsInView(true);
|
||||||
|
if (options.triggerOnce) {
|
||||||
|
observerManager.unobserve(element);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const observer = new IntersectionObserver(
|
const callback = (entry: IntersectionObserverEntry) => {
|
||||||
(entries) => {
|
if (entry.isIntersecting) {
|
||||||
entries.forEach((entry) => {
|
setIsInView(true);
|
||||||
if (entry.isIntersecting) {
|
if (options.triggerOnce) {
|
||||||
setIsVisible(true);
|
observerManager.unobserve(element);
|
||||||
if (triggerOnce) {
|
}
|
||||||
observer.unobserve(entry.target);
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
{ threshold },
|
|
||||||
);
|
|
||||||
|
|
||||||
observer.observe(element);
|
observerManager.observe(element, callback);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (element) observer.unobserve(element);
|
observerManager.unobserve(element);
|
||||||
};
|
};
|
||||||
}, [ref, threshold, triggerOnce]);
|
}, [observerManager]);
|
||||||
|
|
||||||
return isVisible;
|
return [elementRef, isInView];
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useInView;
|
export default useInView;
|
||||||
|
63
src/hooks/useInViewsingle.tsx
Normal file
63
src/hooks/useInViewsingle.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;
|
Loading…
x
Reference in New Issue
Block a user