Perspective Carousel
3D 透視輪播(coverflow 風格),支援拖曳翻頁、循環、自動輪播與受控/非受控雙模式。
載入預覽⋯
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
1200
45
120
2
0.12
<PerspectiveCarousel />
安裝
npx shadcn@latest add https://webberui.com/r/perspective-carousel.json或在 components.json 設定 registries 後,改用 @webberui/perspective-carousel 安裝。
安裝依賴後,從 registry JSON(/r/perspective-carousel.json 的 files[0].content)複製 perspective-carousel.tsx 原始碼到你的 components/ui/ 目錄:
npm install motion lucide-react clsx tailwind-merge使用
import { PerspectiveCarousel } from "@/components/ui/perspective-carousel";
const items = [
{ id: "a", content: <img src="/1.jpg" alt="" className="h-full w-full object-cover" /> },
{ id: "b", content: <img src="/2.jpg" alt="" className="h-full w-full object-cover" /> },
{ id: "c", content: <img src="/3.jpg" alt="" className="h-full w-full object-cover" /> },
];
<PerspectiveCarousel items={items} loop />受控模式
傳入 index 與 onIndexChange 即進入受控模式,可與外部狀態(如指示點、縮圖列)同步:
const [active, setActive] = React.useState(0);
<PerspectiveCarousel items={items} index={active} onIndexChange={setActive} loop />程式化操作
透過 ref 取得 next / prev / goTo:
const ref = React.useRef<PerspectiveCarouselHandle>(null);
<PerspectiveCarousel ref={ref} items={items} />
<button onClick={() => ref.current?.next()}>下一張</button>Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
items | PerspectiveCarouselItem[] | — | 投影片陣列,每項含 id 與 content |
index | number | — | 受控目前索引(0 起算),提供時由外部掌控 |
defaultIndex | number | 0 | 非受控初始索引 |
onIndexChange | (index: number) => void | — | 索引變動時觸發 |
loop | boolean | false | 是否循環(頭尾相接的環狀 3D 排列) |
itemWidth | number | 240 | 投影片寬度(px) |
itemHeight | number | 320 | 投影片高度(px) |
visible | number | 2 | 每側可見的鄰片數量 |
perspective | number | 1200 | CSS 透視距離(px),越小透視越強 |
rotate | number | 45 | 鄰片繞 Y 軸旋轉角度(deg) |
depth | number | 120 | 每階往後推的 Z 位移(px) |
spread | number | itemWidth * 0.6 | 每階水平間距(px) |
scaleStep | number | 0.12 | 每階縮小比例 |
autoplay | boolean | false | 自動輪播(減少動態時停用) |
autoplayInterval | number | 3500 | 自動輪播間隔(ms) |
showArrows | boolean | true | 是否顯示左右箭頭 |
velocityThreshold | number | 320 | 觸發翻頁的甩動速度閾值(px/s) |
ariaLabel | string | "3D 透視輪播" | 輪播區的無障礙標籤 |
型別
interface PerspectiveCarouselItem {
id: string | number;
content: React.ReactNode;
}
interface PerspectiveCarouselHandle {
next: () => void;
prev: () => void;
goTo: (index: number) => void;
}細節
- 純 CSS 3D transform(
perspective+rotateY+translateZ)打造 coverflow 透視,未依賴任何 WebGL 套件。 - 所有投影片共用一個浮點位置 motion value,中心到兩側的位移、旋轉、縮放、透明度與壓暗皆由該值連續映射,拖曳過程即時跟手。
- 拖曳放開時依落點四捨五入翻頁,甩動速度超過
velocityThreshold則往甩動方向再進一張。 loop開啟時採最短環狀路徑落定,並在動畫結束後將位置正規化,長時間輪播不會累積漂移。
可及性
- 輪播容器帶
role="group"、aria-roledescription="輪播"與可聚焦的tabIndex,方向鍵可翻頁,Home / End 直達首末張。 - 目前張數以
aria-live="polite"的隱藏區域朗讀(「第 N 張,共 M 張」);非當前投影片標記aria-hidden。 - 左右箭頭為原生
button,附aria-label並在到達邊界(非循環)時disabled。 - 使用者系統開啟「減少動態效果」時,停用拖曳與自動輪播,翻頁改為即時切換不含補間動畫。
- 指標懸停或焦點進入輪播區時自動暫停自動輪播。