WebberUI

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.jsonfiles[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型別預設值說明
framesstring[]依序播放的影格網址,捲動進度對應到陣列索引
scrollScalenumber3捲動軌道為釘住畫布高度的倍數,越大刷動越細膩
offsetTopnumber0釘住畫布的吸頂位置(px)
fit"cover" | "contain""cover"影像填充方式:填滿裁切或完整置中
smoothbooleanfalse是否以彈簧平滑刷動(帶慣性但略微延遲)
posterIndexnumber0reduced-motion 時作為靜態海報的影格索引
backgroundstringcontain 留白處的填色(CSS 色值),預設透明
containerRefObject<HTMLElement>巢狀捲動容器的 ref;預設以視窗為捲動容器
altstring"捲動刷動的影像序列"畫布的無障礙標籤
onProgress(progress, frameIndex) => void刷動時回呼進度(0–1)與影格索引
onLoadProgress(loaded, total) => void影格預載進度回呼
stickyClassNamestring"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 事件、requestAnimationFrameResizeObserver 皆在卸載時完整清理。

On this page