Scroll Progress Set
滾動進度三合一:頂部進度條、右下圓環與右側章節節點,共用同一個捲動進度。
載入預覽⋯
安裝
npx shadcn@latest add https://webberui.com/r/scroll-progress-set.json或在 components.json 設定 registries 後,改用 @webberui/scroll-progress-set 安裝。
安裝依賴後,從 registry JSON(/r/scroll-progress-set.json 的 files[0].content)複製 scroll-progress-set.tsx 原始碼到你的 components/ui/ 目錄:
npm install motion clsx tailwind-merge使用
ScrollProgress 以 useScroll 追蹤捲動進度,可切換為三種呈現形式。傳入 container 時會偵測該容器的內部捲動,並把指示器貼齊容器可視區;未傳入時則追蹤整個視窗。
import {
ScrollProgress,
type ScrollSection,
} from "@/components/ui/scroll-progress-set";
// 追蹤整個頁面的頂部進度條
<ScrollProgress variant="bar" />
// 追蹤某個 overflow 容器
const scrollerRef = React.useRef<HTMLDivElement>(null);
<div ref={scrollerRef} className="h-[400px] overflow-y-auto">
<ScrollProgress variant="ring" container={scrollerRef} />
{/* 內容 */}
</div>dots 變體需要提供 sections,其 id 必須對應頁面中同名元素的 id:
const sections: ScrollSection[] = [
{ id: "intro", label: "簡介" },
{ id: "usage", label: "使用" },
];
<ScrollProgress variant="dots" container={scrollerRef} sections={sections} />
<section id="intro">…</section>
<section id="usage">…</section>Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
variant | "bar" | "ring" | "dots" | "bar" | 進度呈現形式:頂部細條、右下圓環、右側章節節點 |
container | RefObject<HTMLElement | null> | — | 巢狀捲動容器的 ref;未提供時以視窗為捲動容器 |
sections | ScrollSection[] | [] | dots 變體的章節清單,id 需對應頁面元素、label 為可選標籤 |
className | string | — | 附加到最外層容器的 class |
細節
- 共用進度:三種變體都綁定同一個
scrollYProgress。bar以scaleX(origin-left)伸展,ring以strokeDashoffset收合並在中央顯示百分比,dots依章節可見比例點亮當前段。 - 容器模式:傳入
container時,以ResizeObserver量測容器可視高度,並用一個sticky錨點承載覆蓋層,讓指示器在容器內捲動時始終貼齊可視區;量測完成前不繪製,避免錯位。 - 章節偵測:
dots使用IntersectionObserver觀察各章節元素,選出可見比例最高者為當前段;作用中的節點放大,並以layoutId讓外圈指示在節點之間連續滑動。 - 彈簧平滑:進度值經
useSpring平滑,捲動停止後仍會柔順地追上目標。 - 元件將
IntersectionObserver、ResizeObserver與 Motion 事件監聽在卸載時全數清理。
可及性
bar與ring帶role="progressbar"與即時更新的aria-valuenow(0–100)。dots是nav內的可聚焦按鈕,帶aria-label與aria-current;點擊即平滑捲動至對應段落,並提供鍵盤聚焦環。- 使用者系統開啟「減少動態效果」時,進度仍隨捲動更新,只是省去彈簧平滑與平滑捲動。