Frontend Lab
← 回到所有模式
PD11📊 Data Display · 資料展示輪播

Apple Carousel

Apple 風格輪播

難度 ★★★★標籤 apple · snap · 中央放大3 min read

模式簡述

Apple Carousel:中央項目放大、兩側縮小淡出。apple.com 產品頁、App Store 都這麼用。視覺很有 brand 感。

第 1 張|首張置中

Claude Code Prompt

💬 Claude Code Prompt
參考:PD11 apple-carousel
組件:AppleCarousel.tsx

Apple Carousel:中央項目放大、兩側縮小淡出。apple.com 產品頁、App Store 都這麼用。視覺很有 brand 感。

限制:
- scale 太誇張
- 沒做 snap
- 忘了 transition

完整程式碼

AppleCarousel.tsx
"use client";
import { useEffect, useState } from "react";
import { cn } from "@/lib/utils";

type State = "first" | "middle" | "dragging";

const ITEMS = [
{ title: "iPhone 17 Pro", subtitle: "鈦金屬", color: "from-zinc-700 to-zinc-900" },
{ title: "MacBook Air", subtitle: "M5 晶片", color: "from-slate-300 to-slate-500" },
{ title: "Vision Pro", subtitle: "空間運算", color: "from-purple-700 to-indigo-900" },
{ title: "iPad", subtitle: "Liquid Retina", color: "from-sky-400 to-blue-600" },
{ title: "Apple Watch", subtitle: "Series 12", color: "from-red-500 to-rose-700" },
];

export function AppleCarousel({ state = "first" }: { state?: State }) {
const idx = state === "middle" ? 2 : state === "dragging" ? 1 : 0;
const [active, setActive] = useState(idx);
useEffect(() => setActive(idx), [idx]);
const dragOffset = state === "dragging" ? -40 : 0;

return (
  <div className="w-full max-w-2xl">
    <div className="relative overflow-hidden rounded-xl">
      <div
        className="flex items-center gap-4 px-[calc(50%-7rem)] py-6 transition-transform duration-500 ease-out"
        style={{ transform: `translateX(calc(-${active * 240}px + ${dragOffset}px))` }}
      >
        {ITEMS.map((it, i) => {
          const d = Math.abs(i - active);
          const scale = d === 0 ? 1 : d === 1 ? 0.85 : 0.7;
          const opacity = d === 0 ? 1 : d === 1 ? 0.7 : 0.4;
          return (
            <button
              key={i}
              onClick={() => setActive(i)}
              style={{ transform: `scale(${scale})`, opacity }}
              className={cn(
                "relative h-56 w-56 shrink-0 overflow-hidden rounded-2xl transition-all duration-500 ease-out",
                `bg-gradient-to-br ${it.color}`,
                d === 0 && "shadow-2xl",
              )}
            >
              <div className="absolute inset-x-0 bottom-0 p-4 text-left text-white">
                <p className="text-[11px] uppercase tracking-wider opacity-70">{it.subtitle}</p>
                <p className="mt-0.5 text-base font-medium">{it.title}</p>
              </div>
            </button>
          );
        })}
      </div>
    </div>
    <div className="mt-3 flex justify-center gap-1.5">
      {ITEMS.map((_, i) => (
        <button
          key={i}
          onClick={() => setActive(i)}
          className={cn(
            "h-1 rounded-full transition-all",
            i === active ? "w-6 bg-lab-accent" : "w-1 bg-muted-foreground/40",
          )}
        />
      ))}
    </div>
  </div>
);
}

何時用

  • ✅ 產品展示頁
  • ✅ 電商高質感商品輪播
  • ✅ 想營造 Apple 風格的 portfolio

何時別用

  • ❌ 後台、工具型介面(太炫)
  • ❌ 資訊型內容(中央放大但旁邊看不清)
  • ❌ 極簡 brand(會破壞調性)

我踩過的坑

  1. scale 太誇張:中央 1.5x 兩側 0.7x 太跳。建議 1.0 / 0.85 / 0.7。

  2. 沒做 snap:手指放開後不對齊任何一項。scroll-snap-type: x mandatory。

  3. 忘了 transition:scale/opacity 切換要 transition-all 500ms ease-out。

相關模式

  • PD09
  • PD12

搜尋

按 ⌘K 隨時開啟