PD09▸📊 Data Display · 資料展示▸輪播
Carousel
輪播
難度 ★★★★★標籤 carousel · swipe · indicator3 min read
模式簡述
Carousel:水平捲動的圖片或卡片列。電商商品、文章推薦。手機端要支援 swipe。
幻燈片一
幻燈片二
幻燈片三
幻燈片四
幻燈片五
Claude Code Prompt
參考:PD09 carousel 組件:Carousel.tsx Carousel:水平捲動的圖片或卡片列。電商商品、文章推薦。手機端要支援 swipe。 限制: - 沒做手機 swipe - 自動播放沒暫停 - indicator 只顯示位置不可點
完整程式碼
"use client";
import { useEffect, useState } from "react";
import { ChevronLeft, ChevronRight } from "lucide-react";
import { cn } from "@/lib/utils";
type State = "first" | "middle" | "last";
const SLIDES = [
{ color: "from-lab-accent to-pink-500", title: "幻燈片一" },
{ color: "from-blue-500 to-cyan-500", title: "幻燈片二" },
{ color: "from-emerald-500 to-teal-500", title: "幻燈片三" },
{ color: "from-purple-500 to-pink-500", title: "幻燈片四" },
{ color: "from-orange-500 to-yellow-500", title: "幻燈片五" },
];
export function Carousel({ state = "first" }: { state?: State }) {
const idx = state === "first" ? 0 : state === "middle" ? 2 : SLIDES.length - 1;
const [active, setActive] = useState(idx);
useEffect(() => setActive(idx), [idx]);
return (
<div className="w-full max-w-md space-y-2">
<div className="relative overflow-hidden rounded-lg border border-border">
<div
className="flex transition-transform duration-500 ease-out"
style={{ transform: `translateX(-${active * 100}%)` }}
>
{SLIDES.map((s, i) => (
<div
key={i}
className={`grid h-44 w-full shrink-0 place-items-center bg-gradient-to-br ${s.color}`}
>
<span className="text-lg font-medium text-white drop-shadow">{s.title}</span>
</div>
))}
</div>
<button
onClick={() => setActive((a) => Math.max(0, a - 1))}
disabled={active === 0}
className="absolute left-2 top-1/2 grid h-8 w-8 -translate-y-1/2 place-items-center rounded-full bg-background/70 backdrop-blur disabled:opacity-30"
>
<ChevronLeft className="h-4 w-4" />
</button>
<button
onClick={() => setActive((a) => Math.min(SLIDES.length - 1, a + 1))}
disabled={active === SLIDES.length - 1}
className="absolute right-2 top-1/2 grid h-8 w-8 -translate-y-1/2 place-items-center rounded-full bg-background/70 backdrop-blur disabled:opacity-30"
>
<ChevronRight className="h-4 w-4" />
</button>
</div>
<div className="flex justify-center gap-1.5">
{SLIDES.map((_, i) => (
<button
key={i}
onClick={() => setActive(i)}
className={cn(
"h-1.5 rounded-full transition-all",
i === active ? "w-6 bg-lab-accent" : "w-1.5 bg-muted-foreground/40",
)}
/>
))}
</div>
</div>
);
}何時用
- ✅ 商品 gallery
- ✅ 首頁推薦內容區
- ✅ feature 比較
何時別用
- ❌ 首屏 hero(自動播放害無障礙閱讀)
- ❌ 重要資訊(使用者根本不會等到看完)
- ❌ 資料超過 20 項(用 grid 或 list)
我踩過的坑
-
沒做手機 swipe:桌面好用但手機點箭頭很彆扭。用 framer-motion drag。
-
自動播放沒暫停:滑鼠 hover 應該暫停,至少要有手動暫停。
-
indicator 只顯示位置不可點:使用者想跳到第 5 張要按 5 次。dot 要可點。
相關模式
- PD11
- PD12