網頁簡報
網頁簡報引擎
16:9 網頁簡報容器 React 元件:等比縮放、鍵盤/觸控翻頁、四種轉場、進度條與頁碼、講者備註、網址同步頁碼與列印成每頁一張,內建九種版面子元件。
把簡報直接做成網頁的一頁。投影片以設計稿尺寸(預設 1280×720)排版,再依容器寬高用 transform: scale() 等比縮放,字級與間距不會因螢幕大小變形。支援鍵盤 ← → Space Home End 與觸控左右滑動翻頁、F 全螢幕、N 講者備註、? 快捷鍵說明;換頁有 slide/fade/zoom/none 四種轉場,底部進度條與逐位滾動的頁碼即時更新。開啟 syncHash 後頁碼寫進網址(#3 為第 3 頁),列印或「另存 PDF」時每頁剛好一張投影片。另附 TitleSlide、StatsSlide、CodeSlide 等九種版面子元件,丟進去就是一份完整簡報。
把這個效果裝進你的專案
在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。
npx webberui@latest add slide-deck-shellPlayground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
400
編輯中的呼叫預設屬性
<SlideDeckShell/>
完整範例包含預覽的排版與資料。先安裝元件,再把範例檔貼入你的專案。
安裝
npx webberui@latest add https://webberui.com/r/slide-deck-shell.json或在 components.json 設定 registries 後,改用 @webberui/slide-deck-shell 安裝。
使用
import {
SlideDeckShell,
TitleSlide,
ListSlide,
StatsSlide,
ClosingSlide,
} from "@/components/ui/slide-deck-shell";
<SlideDeckShell label="Q3 產品回顧" transition="slide" syncHash>
<TitleSlide
eyebrow="2026 Q3"
title="Q3 產品回顧"
subtitle="三個月、兩次大改版"
notes="開場先自我介紹"
/>
<ListSlide
title="三項主要成果"
ordered
items={["編輯器重寫", "團隊素材庫", "行動版審稿"]}
/>
<StatsSlide
title="關鍵數字"
items={[
{ value: 12480, label: "月活躍使用者" },
{ value: 64, suffix: "%", label: "30 日留存率" },
]}
/>
<ClosingSlide title="謝謝大家" contact="hello@example.com" />
</SlideDeckShell>
// 也可以用資料驅動(slides 優先於 children)
<SlideDeckShell
slides={[
{ id: "cover", content: <TitleSlide title="封面" />, notes: "備註" },
{ id: "end", content: <ClosingSlide title="結束" /> },
]}
/>Props
SlideDeckShell
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
children | React.ReactNode | — | 投影片(<Slide> 或內建版面元件);與 slides 擇一,slides 優先 |
slides | DeckSlideData[] | — | 資料驅動的投影片陣列;提供時忽略 children |
index | number | — | 受控的目前頁索引(0 起算);未提供時由元件內部管理 |
defaultIndex | number | 0 | 非受控模式的初始頁索引(0 起算) |
onIndexChange | (index: number) => void | — | 頁索引變動時回呼(0 起算) |
transition | "slide" | "fade" | "zoom" | "none" | "slide" | 換頁轉場;系統開啟減少動態效果時一律改為短淡入 |
aspectRatio | number | 16 / 9 | 投影片寬高比 |
baseWidth | number | 1280 | 設計稿寬度(px);投影片以此尺寸排版,再依容器等比縮放 |
showProgress | boolean | true | 顯示底部進度條 |
showCounter | boolean | true | 顯示頁碼 |
syncHash | boolean | false | 以網址 hash(#3 為第 3 頁)同步頁碼;預設關,避免同頁多個簡報互相搶網址 |
notes | boolean | — | 受控的講者備註面板開關;未提供時由元件內部管理 |
defaultNotes | boolean | false | 非受控模式下備註面板是否預設展開 |
onNotesChange | (open: boolean) => void | — | 備註面板開關變動時回呼 |
keyboard | "focus" | "global" | "focus" | focus 只在簡報聚焦或全螢幕時接收按鍵;global 監聽整個視窗 |
label | string | "簡報" | 簡報名稱,作為根元素的 aria-label |
labels | Partial<SlideDeckShellLabels> | — | 介面文字覆寫 |
className | string | — | 透傳到最外層容器 |
DeckSlideData
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
id | string | — | 穩定識別碼,作為 React key;未提供時用索引 |
content | React.ReactNode | — | 投影片內容(通常是版面元件) |
notes | React.ReactNode | — | 講者備註 |
SlideDeckShellLabels
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
prev | string | "上一頁" | 上一頁按鈕 |
next | string | "下一頁" | 下一頁按鈕 |
fullscreen | string | "全螢幕" | 進入全螢幕按鈕 |
exitFullscreen | string | "離開全螢幕" | 離開全螢幕按鈕 |
notes | string | "講者備註" | 講者備註按鈕與面板標題 |
shortcuts | string | "快捷鍵" | 快捷鍵按鈕與說明層標題 |
close | string | "關閉" | 關閉說明層按鈕 |
progress | string | "簡報進度" | 進度條的無障礙名稱 |
noNotes | string | "這一頁沒有備註" | 當頁沒有備註時的提示 |
deck | string | "簡報" | 根元素的 aria-roledescription |
slide | string | "投影片" | 每頁的 aria-roledescription |
page | (current: number, total: number) => string | 第 N 頁,共 M 頁 | 頁碼描述,用於每頁 aria-label 與翻頁朗讀 |
版面子元件
所有版面元件都接受 notes(講者備註)、background(任何 CSS background 值)與 className。
| 元件 | 主要 props | 說明 |
|---|---|---|
Slide | children | 空白頁,以設計稿尺寸滿版排版,自行放內容 |
TitleSlide | eyebrow、title、subtitle | 封面頁 |
SectionSlide | number、title、description | 章節分隔頁 |
TwoColumnSlide | title、left、right、ratio(預設 [1, 1]) | 雙欄頁 |
ImageFullSlide | src、alt、title、caption | 滿版圖片(自備網址)或漸層,底部疊字卡 |
StatsSlide | title、items: DeckStat[]、locale(預設 "zh-TW") | 數據頁,進場時從 0 計數 1.2 秒 |
QuoteSlide | quote、author、role | 引言頁 |
ListSlide | title、items、ordered | 條列頁,逐條 stagger 80ms 淡入 |
CodeSlide | title、code、language、filename、copyLabel、copiedLabel | 程式碼頁:等寬字、行號、複製鈕 |
ClosingSlide | title、subtitle、contact | 結尾頁 |
DeckStat
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
value | number | — | 目標數值 |
label | React.ReactNode | — | 數據說明 |
prefix | string | — | 數字前綴(例如 NT$) |
suffix | string | — | 數字後綴(例如 %) |
細節
- 縮放:以 ResizeObserver 量測容器,縮放比取「容器寬 ÷
baseWidth」與「容器高 ÷ 設計稿高」的較小者,全螢幕時自動上下或左右留邊。投影片裡可以直接用 px 與一般 Tailwind 字級排版。 - 轉場:
AnimatePresence mode="wait",退場與進場依序播放,整次換頁的總時長是--wb-deck-duration(預設400ms),曲線是--wb-deck-ease(預設cubic-bezier(0.22,1,0.36,1))。兩個變數在換頁前從 CSS 讀取,可在任何祖先元素覆寫。強調色用--wb-deck-accent,預設跟隨文字色。 - 鍵盤:預設
keyboard="focus",只有點過簡報(或 Tab 聚焦)後才接收方向鍵與 Space,不會搶走頁面捲動;全螢幕時改為監聽整個視窗。輸入框裡的按鍵一律放行。 - 觸控:水平滑動超過 50px,或速度超過 0.3px/ms 即翻頁;垂直滑動交給頁面捲動(
touch-action: pan-y)。 - 全螢幕:F 鍵或按鈕呼叫 Fullscreen API,瀏覽器不支援時隱藏按鈕。Esc 先關閉快捷鍵說明;全螢幕下的 Esc 由瀏覽器處理退出。
- 講者備註:顯示在簡報下方,全螢幕時改為右側欄。
children模式從每個子元件的notesprop 收集。 - 列印:
@media print下隱藏控制列、停用縮放與轉場,所有投影片依序輸出,紙張尺寸設為投影片尺寸,「另存 PDF」時每頁剛好一張。這段@page規則在元件掛載期間對整個頁面生效,建議簡報放在獨立路由。 - 不做 PPTX 匯出:本元件是純前端元件,需要 PPTX 請先用瀏覽器列印成 PDF,或另行轉檔。
CodeSlide只做等寬字、行號與複製鈕,不做語法高亮;需要高亮可以把內容換成 WebberUI 的code-block。
可及性
- 根元素為
role="region"、aria-roledescription="簡報"並以label命名;每頁為role="group"、aria-roledescription="投影片",aria-label如「第 3 頁,共 8 頁」 - 只渲染目前這一頁,退場中的頁設為
inert並對輔助科技隱藏 - 翻頁時
aria-live="polite"區域朗讀「第 N 頁,共 M 頁」;底部進度條為role="progressbar" - 所有控制鈕都是
type="button"、帶aria-label與 focus-visible 外框;備註按鈕帶aria-pressed - 快捷鍵說明為
role="dialog"、aria-modal,開啟時焦點移到關閉鈕並鎖在對話框內,關閉後焦點回到原處 - 使用者系統開啟「減少動態效果」時,所有轉場改為 150ms 純淡入,數字直接顯示終值、條列不做 stagger、頁碼不滾動