Skip to content

Instantly share code, notes, and snippets.

@roniardev
Created June 12, 2024 02:05
Show Gist options
  • Save roniardev/24d3c93cd76f38536a0d587fd3e50989 to your computer and use it in GitHub Desktop.
Save roniardev/24d3c93cd76f38536a0d587fd3e50989 to your computer and use it in GitHub Desktop.

Revisions

  1. roniardev created this gist Jun 12, 2024.
    130 changes: 130 additions & 0 deletions ImgCarousel.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,130 @@
    import { useEffect, useState, useCallback } from "react";
    import { motion } from "framer-motion";
    import { Icon } from "@iconify/react";

    type Props = {
    images: string[];
    isAutoPlay?: boolean;
    };

    const variants = {
    hidden: {
    filter: "blur(15px)",
    opacity: 0,
    transition: {
    duration: 0.85,
    ease: "easeInOut",
    },
    },
    show: {
    filter: "blur(0px)",
    opacity: 1,
    transition: {
    duration: 0.85,
    ease: "easeInOut",
    },
    },
    };

    export default function ImgCarousel({ images, isAutoPlay = false }: Props) {
    const [current, setCurrent] = useState(0);
    const [animate, setAnimate] = useState<"INITIAL" | "HIDDEN">(
    "INITIAL"
    ); // State to control animation

    const previousSlide = () => {
    setAnimate("HIDDEN"); // Hide before transitioning
    setTimeout(() => {
    setCurrent((prev) => (prev === 0 ? images.length - 1 : prev - 1));
    setAnimate("INITIAL"); // Show after transitioning
    }, 526); // Half of transition duration for smooth effect
    };

    const nextSlide = useCallback(() => {
    setAnimate("HIDDEN");
    setTimeout(() => {
    setCurrent((prev) => (prev === images.length - 1 ? 0 : prev + 1));
    setAnimate("INITIAL");
    }, 526);
    }, [images]);

    useEffect(() => {
    if (isAutoPlay) {
    const interval = setInterval(() => {
    nextSlide();
    }, 3000);
    return () => clearInterval(interval);
    }
    }, [isAutoPlay, nextSlide]);

    return (
    <main className="container mx-auto">
    <div className="relative w-full h-96 p-12">
    <div className="absolute inset-0 flex items-center justify-center">
    <motion.img
    src={images[current]}
    alt="carousel"
    className="object-cover w-full h-full"
    initial="hidden"
    animate={
    animate === "INITIAL" ? "show" : "hidden"
    }
    variants={variants}
    />
    </div>
    <div className="absolute inset-y-0 right-0 flex items-center justify-center px-5">
    <button
    onClick={nextSlide}
    className="p-2 bg-red-800 bg-opacity-80 rounded-md"
    title="Next"
    >
    <Icon
    icon="solar:arrow-right-linear"
    className="w-6 h-6 text-white"
    />
    </button>
    </div>
    <div className="absolute inset-y-0 left-0 flex items-center justify-center px-5 rounded-full">
    <button
    onClick={previousSlide}
    className="p-2 bg-red-800 bg-opacity-80 rounded-md"
    title="Previous"
    >
    <Icon
    icon="solar:arrow-left-linear"
    className="w-6 h-6 text-white"
    />
    </button>
    </div>
    </div>
    <div className="flex flex-row mt-12 gap-3 align-middle justify-center items-center">
    {images.map((_, index) => (
    <button>
    {
    current === index ? (
    <Icon
    key={index}
    icon="octicon:dot-fill-24"
    className="w-6 h-6 text-gray-800"
    />
    ) : (
    <Icon
    key={index}
    icon="octicon:dot-24"
    className="w-6 h-6 text-gray-800"
    onClick={() => {
    setAnimate("HIDDEN");
    setTimeout(() => {
    setCurrent(index);
    setAnimate("INITIAL");
    }, 526);
    }}
    />
    )
    }
    </button>
    ))}
    </div>
    </main>
    );
    }