媒體

卡帶播放器

擬物卡帶外觀的音訊播放器:捲軸隨播放時間旋轉、兩側磁帶量隨進度增減,倒帶動畫、可拖曳進度條與字幕軌。

這是 WebberUI Pro 元件

下方的安裝指令需要授權才能執行,未授權時會回 401。

怎麼安裝 Pro 元件 →查看方案 →

一台看得見磁帶在走的音訊播放器。捲軸以 requestAnimationFrame 直接讀 audio.currentTime 旋轉,左側供帶的磁帶量隨進度變小、右側收帶變大;按倒帶時顯示時間依距離長短快速轉回開頭,捲軸跟著反轉。進度條可拖曳、點擊或用鍵盤跳轉,另有快轉 10 秒、靜音、音量與字幕軌。上方標籤紙寫著曲名、副標、編目號碼與 A/B 面,三色條可自訂。沒有傳 audioSrc 時,元件會在瀏覽器端合成一段約 12 秒的短旋律,不需要任何音檔就能試玩;正式使用時傳入你自己的音訊網址即可。

把這個效果裝進你的專案

在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。

npx webberui@latest add "https://webberui.com/r/cassette-player.json?t=<安裝 token>"

還沒有專案,或安裝遇到問題?看快速開始 →

Playground

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

0.8
1
編輯中的呼叫預設屬性
<CassettePlayer/>

完整範例包含預覽的排版與資料。先安裝元件,再把範例檔貼入你的專案。

安裝

npx webberui@latest add "https://webberui.com/r/cassette-player.json?t=<安裝 token>"

或在 components.json 設定 registries 後,改用 @webberui/cassette-player 安裝。

使用

import { CassettePlayer } from "@/components/ui/cassette-player";

<CassettePlayer
  audioSrc="/audio/afternoon-walk.mp3"
  trackTitle="午後散步"
  archiveLabel="家庭錄音帶 1987"
  catalogueNumber="WB-0042"
  sideLabel="A"
  captionTracks={[
    {
      vtt: "WEBVTT\n\n00:00.000 --> 00:04.000\n午後的巷口,風把曬衣繩吹得輕輕搖晃。\n",
      srclang: "zh-Hant",
      label: "繁體中文",
      default: true,
    },
  ]}
  onPlaybackChange={(state) => console.log(state.playing, state.time)}
/>;

Props

Prop型別預設值說明
audioSrcstring—音訊網址;未提供時使用內建合成的約 12 秒短旋律(依 sideLabel 換曲),僅供示範
captionTracksCassetteCaptionTrack[]—字幕軌清單;有字幕時控制列出現字幕開關
trackTitlestring—曲名,寫在標籤紙上,也作為播放器的 aria-label
archiveLabelstring—標籤紙副標,例如錄音來源或年份
catalogueNumberstring—編目號碼,以等寬字顯示在標籤右上
sideLabelstring"A"卡帶面別,以大字顯示在標籤左側
initialVolumenumber0.8初始音量(0–1),只在掛載時讀取
loopbooleanfalse播完是否從頭循環
reelSpeednumber1捲軸轉速倍率;1 為每秒 300 度
stripes[string, string, string]["#e2583e", "#f2a541", "#3f88c5"]標籤紙三色條(由上而下);未傳時讀 --wb-cassette-stripe-1..3 變數,變數未設才用預設色
onPlaybackChange(state: CassettePlaybackState) => void—播放、暫停、跳轉、載入完成時回呼
grainbooleanfalse在外殼疊一層細顆粒噪點
classNamestring—透傳到最外層容器

CassetteCaptionTrack

欄位型別預設值說明
srcstring—WebVTT 檔案網址
vttstring—WebVTT 原文字串(含 WEBVTT 標頭),會自動轉成 Blob URL;提供時優先於 src
srclangstring—語言代碼,例如 zh-Hant
labelstring—字幕軌名稱
defaultbooleanfalse是否為預設字幕軌;有任一軌為 true 時字幕預設開啟

CassettePlaybackState

欄位型別預設值說明
playingboolean—是否正在播放
timenumber—目前播放時間(秒)
durationnumber—音訊總長(秒);尚未載入時為 0

另外具名匯出 CassettePlayerProps、CassetteCaptionTrack 與 CassettePlaybackState 型別。

細節

  • 同步迴圈:播放中以 requestAnimationFrame 讀 audio.currentTime,不靠 timeupdate(約 4Hz,捲軸會跳格)。捲軸角度 = 時間 × 300 × reelSpeed(取 360 的餘數);供帶磁帶量由 1 線性縮到 0.6、收帶由 0.6 長到 1。這些值寫進 motion value,不會每幀觸發 React 重新渲染
  • 倒帶:暫停同步迴圈與播放,把顯示時間從目前位置動畫回 0,時長 = 220ms + 每秒距離 40ms,上限 1 秒;捲軸隨之快速反轉。結束後 currentTime 設為 0,若原本在播放就續播
  • 內建合成音源:未傳 audioSrc 時,元件以純 JS 逐樣本合成 24 個音符(正弦+三角波、attack/release 包絡、少量磁帶嘶聲與轉速抖動),編碼成 WAV Blob 後餵給同一個 audio 元素,所以跳轉、倒帶、循環的行為和真實音檔完全相同;卸載時 revoke Blob URL。A 面與 B 面會得到不同的旋律
  • 字幕:vtt 字串會轉成 Blob URL 放進 track,選定的字幕軌設為 hidden 載入,元件自己讀 activeCues 顯示在外殼下方
  • 主題變數:--wb-cassette-duration(按鈕轉場,預設 150ms)、--wb-cassette-ease(預設 ease-out)、--wb-cassette-stripe-1..3(三色條)、--wb-cassette-paper(標籤紙色)、--wb-cassette-tape(磁帶色)
  • 標籤紙文字以容器寬度(cqw)縮放,播放器多寬標籤就多大

可及性

  • 外層為 role="group",aria-label 為曲名;卡帶外殼純裝飾(aria-hidden),audio 元素不顯示原生控制列
  • 所有控制都是原生 button,附繁中 aria-label(播放/暫停、倒帶、快轉 10 秒、靜音、字幕);靜音與字幕開關帶 aria-pressed,皆有 focus-visible 外框
  • 進度條為 role="slider",aria-valuetext 讀成「1 分 02 秒,共 3 分 10 秒」;鍵盤 ←/→ 前後 5 秒、Home/End 跳到開頭或結尾,指標可拖曳或點擊跳轉
  • 播放狀態變化(播放中、已暫停、倒帶中、播放結束)由 sr-only 的 aria-live 區域播報
  • 使用者系統開啟「減少動態效果」時,捲軸與磁帶量凍結在當下的比例不再轉動,倒帶直接跳回開頭不播動畫,其餘功能照常