Image Sequence Scrub
以捲動進度刷動一組影格序列的產品頁式效果,畫面釘住不動、影像隨捲動逐格播放。
載入預覽⋯
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
3
<ImageSequenceScrub />
安裝
npx shadcn@latest add https://webberui.com/r/image-sequence-scrub.json或在 components.json 設定 registries 後,改用 @webberui/image-sequence-scrub 安裝。
安裝依賴後,從 registry JSON(/r/image-sequence-scrub.json 的 files[0].content)複製 image-sequence-scrub.tsx 原始碼到你的 components/ui/ 目錄:
npm install motion clsx tailwind-merge使用
傳入一組依序排列的影格網址,捲動軌道容器內的畫布會被釘住,並隨捲動進度逐格切換影像。
import { ImageSequenceScrub } from "@/components/ui/image-sequence-scrub";
const frames = Array.from(
{ length: 120 },
(_, i) => `/sequence/frame-${String(i).padStart(4, "0")}.jpg`
);
<ImageSequenceScrub
frames={frames}
scrollScale={4}
fit="cover"
smooth
alt="產品旋轉展示"
stickyClassName="h-screen"
>
<div className="flex h-full items-end p-10">
<h2 className="text-4xl font-bold text-white">Designed to move.</h2>
</div>
</ImageSequenceScrub>若效果放在頁面的巢狀捲動區域內,請把該捲動容器的 ref 傳給 container,並將 stickyClassName 高度設為與容器一致。
Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
frames | string[] | — | 依序播放的影格網址,捲動進度對應到陣列索引 |
scrollScale | number | 3 | 捲動軌道為釘住畫布高度的倍數,越大刷動越細膩 |
offsetTop | number | 0 | 釘住畫布的吸頂位置(px) |
fit | "cover" | "contain" | "cover" | 影像填充方式:填滿裁切或完整置中 |
smooth | boolean | false | 是否以彈簧平滑刷動(帶慣性但略微延遲) |
posterIndex | number | 0 | reduced-motion 時作為靜態海報的影格索引 |
background | string | — | contain 留白處的填色(CSS 色值),預設透明 |
container | RefObject<HTMLElement> | — | 巢狀捲動容器的 ref;預設以視窗為捲動容器 |
alt | string | "捲動刷動的影像序列" | 畫布的無障礙標籤 |
onProgress | (progress, frameIndex) => void | — | 刷動時回呼進度(0–1)與影格索引 |
onLoadProgress | (loaded, total) => void | — | 影格預載進度回呼 |
stickyClassName | string | "h-screen" | 釘住畫布容器的 class,畫布高度由此決定 |
細節
- 影格會在掛載時全部預載為
Image物件,並繪製到單一<canvas>,避免逐格切換<img>src 造成的閃爍。 - 每次捲動只重繪索引有變化的影格,並以
requestAnimationFrame合併多次請求;DPR 縮放與cover/contain版面皆在畫布內計算,ResizeObserver監看畫布尺寸變動後重繪。 - 軌道容器高度由釘住畫布的量測高度乘上
scrollScale得出,因此無論以視窗或巢狀容器捲動都能正確產生刷動距離。 smooth會將捲動進度導入彈簧,適合快速滑動時的順滑感;需要逐格緊貼捲動時關閉即可。
可及性
- 使用者系統開啟「減少動態效果」時,改以單一
posterIndex影格靜態呈現,不進行刷動。 - 畫布帶
role="img"與alt標籤;疊加內容預設不吃指標事件(pointer-events-none),互動元素請自行加上pointer-events-auto。 - 影格載入的
Image事件、requestAnimationFrame與ResizeObserver皆在卸載時完整清理。