[Next.js][React][TypeScript][TailwindCSS] 3.建立輪播

文、意如

建立滿版輪播

使用 Tailwind CSS 做版型

搭配輕量化的輪播套件,如:

keen-slider

安裝套件:npm install keen-slider

程式碼:

src/components/HeroCarousel.tsx

"use client";

import "keen-slider/keen-slider.min.css";
import { useKeenSlider } from "keen-slider/react";
import { useEffect, useRef, useState } from "react";
import { ArrowLeft, ArrowRight } from "lucide-react";

const images = [
  "/images/banner1.jpg",
  "/images/banner2.jpg",
  "/images/banner3.jpg",
];

export default function HeroCarousel() {
  const [currentSlide, setCurrentSlide] = useState(0);
  const timer = useRef<NodeJS.Timeout | null>(null);

  const [sliderRef, instanceRef] = useKeenSlider<HTMLDivElement>({
    loop: true,
    slideChanged(slider) {
      setCurrentSlide(slider.track.details.rel);
    },
  });

  // 自動輪播效果
  useEffect(() => {
    if (!instanceRef.current) return;

    timer.current = setInterval(() => {
      instanceRef.current?.next();
    }, 4000); // 每 4 秒切換

    return () => {
      if (timer.current) clearInterval(timer.current);
    };
  }, [instanceRef]);

  return (
    <div className="relative w-full h-[400px] md:h-[500px] overflow-hidden">
      <div ref={sliderRef} className="keen-slider w-full h-full">
        {images.map((src, idx) => (
          <div key={idx} className="keen-slider__slide">
            <img src={src} alt={`banner-${idx}`} className="w-full h-full object-cover" />
          </div>
        ))}
      </div>

      {/* 左右箭頭 */}
      <button
        onClick={() => instanceRef.current?.prev()}
        className="absolute top-1/2 left-4 -translate-y-1/2 bg-black/60 p-3 rounded-full shadow-lg hover:bg-black hover:scale-110 transition"
      >
        <ArrowLeft className="w-8 h-8 text-white" />
      </button>
      <button
        onClick={() => instanceRef.current?.next()}
        className="absolute top-1/2 right-4 -translate-y-1/2 bg-black/60 p-3 rounded-full shadow-lg hover:bg-black hover:scale-110 transition"
      >
        <ArrowRight className="w-8 h-8 text-white" />
      </button>

      {/* 指示點 */}
      <div className="absolute bottom-4 left-1/2 -translate-x-1/2 flex gap-2">
        {images.map((_, idx) => (
          <button
            key={idx}
            onClick={() => instanceRef.current?.moveToIdx(idx)}
            className={`w-3 h-3 rounded-full ${
              currentSlide === idx ? "bg-white" : "bg-white/50"
            }`}
          ></button>
        ))}
      </div>
    </div>
  );
}

圖片放在這裡:

public/images/banner1.jpg

public/images/banner2.jpg

public/images/banner3.jpg

首頁page.tsx
import Navbar from "@/components/Navbar";
import HeroCarousel from "@/components/HeroCarousel";

export default function Home() {
  return (
    <>
      <Navbar />
      <main className="pt-15 min-h-screen">
        <HeroCarousel />

        <div className="p-8">
          <h1 className="text-3xl font-bold text-black">首頁內容</h1>
        </div>
      </main>
    </>
  );
}

Yiru@Studio - 關於我 - 意如