I have a webpage built with Next.js,TypeScript and tailwindCSS. On this page, i want to show a bunch of cards, each representing a different animal. These cards have various attributes like images and items associated with them.
To manage the layout and functionality, i am using a carousel library called Embla Carousel. It's like a rotating display stand where you can showcase your cards one by one.
To make things interesting,i've added an autoplay feature to the carousel but didn't work, or I don't see why.
this is my page with carousel
import React, { useEffect, useState } from 'react'; import { Cards } from '@/components/Cards/cards-row'; import littleAnimals from '../data/little-animals.json'; import HeaderAnimals from '@/components/Cards/header-Animalitos'; import useEmblaCarousel from 'embla-carousel-react'; import Autoplay from 'embla-carousel-autoplay'; export function useWindowSize() { const isClient = typeof window === 'object'; const [windowSize, setWindowSize] = useState({ width: isClient ? window.innerWidth : undefined, height: isClient ? window.innerHeight : undefined, }); useEffect(() => { function handleResize() { setWindowSize({ width: window.innerWidth, height: window.innerHeight, }); } window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); return windowSize; } const LittleAnimals: React.FC = () => { const windowSize = useWindowSize(); const [emblaRef, emblaApi] = useEmblaCarousel({ loop: true, axis: 'y' }, [ Autoplay({ delay: 10000, }), ]); console.log(emblaRef); console.log(emblaApi); console.log(Autoplay); useEffect(() => { if (emblaApi) { if ((windowSize.width ?? 0) <= 846) { // sm and md sizes emblaApi.reInit({ loop: false, axis: 'y', slidesToScroll: 1 }); } else { emblaApi.reInit({ loop: true, axis: 'y', slidesToScroll: 4 }); } } }, [emblaApi, windowSize]); const divideArray = <T,>(array: T[], chunkSize: number): T[][] => { if (chunkSize > array.length) { return [array]; } const subarrays: T[][] = []; for (let i = 0; i < array.length; i += chunkSize) { const subarray = array.slice(i, i + chunkSize); subarrays.push(subarray); } return subarrays; }; const subArrays = divideArray(littleAnimals, 4); return ( <div className='w-full h-full flex items-center flex-col overflow-x-hidden overflow-y-auto min-h-screen bg-white'> <HeaderAnimals /> <div ref={emblaRef} className='overflow-hidden block sm:h-auto' style={{ height: '100%', overflowY: 'scroll' }} > <div className='carousel-container' style={{ backfaceVisibility: 'hidden', display: 'flex', touchAction: 'pan-Y', flexDirection: 'column', height: '100%', justifyContent: 'center', }} > {subArrays.map((lotteryGroup, groupIndex) => ( <div key={groupIndex} className='flex flex-col md:gap-10 py-5 items-center justify-center ' style={{ flex: '0 0 100%', minHeight: 0, }} > {lotteryGroup.map(loteria => { return ( <div key={loteria.name} className='w-full'> <div className=''/> <Cards image={loteria.image || ''} items={loteria.items} name={loteria.name} /> </div> ); })} </div> ))} </div> </div> </div> ); }; export default LittleAnimals;
and my component is
import React, { useState } from 'react';
import { LazyLoadImage } from 'react-lazy-load-image-component';
import 'react-lazy-load-image-component/src/effects/blur.css';
import { Skeleton } from '@nextui-org/skeleton';
type CardsProps = {
image: string;
name: string;
items: {
appointment: string;
result: number;
image: string;
animal: string;
}[];
};
export function Cards({ image, name, items }: CardsProps) {
const [loaded, setLoaded] = useState<boolean[]>(new Array(items.length).fill(false));
const [showSkeleton, setShowSkeleton] = useState<boolean[]>(new Array(items.length).fill(true));
const handleLoad = (index: number) => {
const newLoaded = [...loaded];
newLoaded[index] = true;
setLoaded(newLoaded);
setTimeout(() => {
const newShowSkeleton = [...showSkeleton];
newShowSkeleton[index] = false;
setShowSkeleton(newShowSkeleton);
}, 2000); // 2 seconds delay
};
return (
<div className='w-full px-7 bg-custom-white border-custom-black border-solid border-8 rounded-[2.5rem] 3xl:rounded-[3rem] 4xl:rounded-[3.5rem] 5xl:rounded-[4rem] '>
{/* Para dispositivos móviles */}
<div className='md:hidden gap-y-15'>
{/* Título de la lotería */}
<div className='flex justify-center text-center mb-2'>
<img src={image} alt={name} className='w-full h-auto' />
</div>
{/* Sorteos */}
<div className='grid grid-cols-3 gap-2'>
{items.map((item, index) => (
<div key={index} className='flex flex-col justify-center mb-2 h-auto'>
<div>
<p className='text-center text-xl font-black text-custom-blue'>{item.appointment}</p>
</div>
<div>
<LazyLoadImage
className='w-full p-2 justify-center align-middle place-content-center'
src={item.image}
alt={item.animal}
effect='blur'
placeholder={showSkeleton[index] ? <Skeleton className='rounded-lg' /> : undefined}
afterLoad={() => handleLoad(index)}
/>
</div>
</div>
))}
</div>
</div>
{/* Para pantallas md o superiores */}
<div className='hidden md:block mb-2 h-auto'>
<div
className='grid grid-cols-1 place-items-center gap-4 sm:grid-cols-12 sm:gap-4 md:grid-cols-12 md:gap-2
lg:gap-10 items-center'
>
{/* Título de la lotería */}
<div className='col-span-12 sm:col-span-2 md:col-span-1 lg:col-span-1 flex justify-center'>
<img src={image} alt={name} className='w-full object-cover object-center' />
</div>
{/* Sorteos */}
{items.map((item, index) => (
<div
key={index}
className='col-span-4 sm:col-span-1 md:col-span-1 lg:col-span-1 flex flex-col justify-center items-center relative h-auto'
>
<div>
<p className='text-xl text-custom-blue font-black'>{item.appointment}</p>
</div>
{!loaded[index] && <Skeleton className='rounded-lg' />}
<div className=''>
<LazyLoadImage
className='w-full justify-center align-middle place-content-center'
src={item.image}
alt={item.animal}
effect='blur'
placeholder={showSkeleton[index] ? <Skeleton className='rounded-lg' /> : undefined}
afterLoad={() => handleLoad(index)}
/>
</div>
<div>
<p className='text-xl text-custom-blue font-black mt-[-15px]'>{item.animal}</p>
</div>
</div>
))}
</div>
</div>
</div>
);
}