WebberUI

3D Rolling Text

滾動驅動的 3D 圓柱文字,行句貼在隱形滾筒上,隨捲動依序翻轉到正面。

載入預覽⋯

Playground

即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。

160
56
900
6
<3dRollingText />

安裝

npx shadcn@latest add https://webberui.com/r/3d-rolling-text.json

或在 components.json 設定 registries 後,改用 @webberui/3d-rolling-text 安裝。

安裝依賴後,從 registry JSON(/r/3d-rolling-text.jsonfiles[0].content)複製 3d-rolling-text.tsx 原始碼到你的 components/ui/ 目錄:

npm install motion clsx tailwind-merge

使用

import { RollingText3D } from "@/components/ui/3d-rolling-text";

<RollingText3D
  lines={["Design", "Animate", "Prototype", "Ship"]}
  lineClassName="text-5xl font-bold tracking-tight"
/>

元件本身是一段可捲動的軌道(總高 = stageHeight + (行數 - 1) * stepHeight),內部以 sticky 舞台固定畫面。放進頁面後,捲過這段軌道即完成一整輪滾筒動畫。

巢狀捲動容器

若動畫發生在 overflow-y: auto 的容器內(而非整頁捲動),把容器的 ref 傳給 container

const scrollerRef = React.useRef<HTMLDivElement>(null);

<div ref={scrollerRef} className="h-[300px] overflow-y-auto">
  <RollingText3D lines={words} container={scrollerRef} />
</div>

Props

Prop型別預設值說明
linesstring[]圓柱面上的文字行,隨滾動依序翻轉到正面
stepHeightnumber160每一行對應的滾動距離(px),越大轉得越慢
stageHeightnumber200吸附舞台(sticky 視窗)的高度(px)
lineHeightnumber56行高(px),同時決定圓柱半徑
perspectivenumber9003D 透視距離(px),越小透視越誇張
direction"up" | "down""up"up 表示新行由下往上滾入正面,down 則由上往下
minFacesnumber6圓柱最少切分的面數;行數少時仍保持滾筒的弧度
containerReact.RefObject<HTMLElement | null>巢狀捲動容器的 ref;預設以視窗為捲動容器
classNamestring套用在整段滾動軌道上的 class
lineClassNamestring套用在每一行文字上的 class(字級、字重、顏色)

細節

  • 圓柱幾何:半徑由 lineHeight / 2 / tan(π / faces) 推得,讓相鄰兩面在滾筒上剛好相接,翻轉時不留縫也不重疊
  • 捲動映射useScroll 取軌道進度,走完全程剛好讓最後一行轉到正面((行數 - 1) × 每面角度),中途停住就停在對應角度,可倒轉
  • 角距淡出:每一行依「與正面的角距」計算透明度——正面全亮、相鄰面半透明、更遠即消失,同時以 backface-visibility: hidden 保底,避免鏡像字
  • 行數少時:以 minFaces 保底切面數,三、四行也能維持滾筒的弧度而不會退化成攤平的翻頁

可及性

  • 使用者系統開啟「減少動態效果」時,放棄 3D 與 sticky,改以靜態直列渲染全部行句
  • 完整文字以 sr-only 提供給輔助科技,3D 視覺層整體 aria-hidden,朗讀時是連續完整的內容
  • 純捲動驅動、無自動播放:動畫進度完全跟隨使用者的捲動操作,隨時可停可倒轉

On this page