Audio Visualizer
Web Audio API 音訊頻譜視覺化:以 canvas 2D 繪製長條、鏡像與圓形三種款式,可分析真實音源或以合成資料驅動示意動畫。
載入預覽⋯
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
48
1
<AudioVisualizer />
安裝
npx shadcn@latest add https://webberui.com/r/audio-visualizer.json或在 components.json 設定 registries 後,改用 @webberui/audio-visualizer 安裝。
安裝依賴後,打開 audio-visualizer.json,把 files[0].content 的內容複製到你的 components/ui/audio-visualizer.tsx:
npm install motion lucide-react clsx tailwind-merge使用
不提供任何音源時,元件以程序化合成資料驅動,適合作為裝飾性的示意動畫:
import { AudioVisualizer } from "@/components/ui/audio-visualizer";
<div className="h-40 overflow-hidden rounded-xl">
<AudioVisualizer variant="bars" />
</div>分析真實媒體元素
把 <audio> / <video> 的 ref 傳給 media,元件會透過 Web Audio API 讀取即時頻譜。媒體聲音會經由音訊圖回放,維持可聽見:
"use client";
import * as React from "react";
import { AudioVisualizer } from "@/components/ui/audio-visualizer";
export function Player() {
const audioRef = React.useRef<HTMLAudioElement>(null);
const [playing, setPlaying] = React.useState(false);
return (
<div>
<audio ref={audioRef} src="/track.mp3" />
<AudioVisualizer
variant="mirror"
media={audioRef}
active={playing}
color="#22d3ee"
className="h-40"
/>
<button
onClick={() => {
const el = audioRef.current;
if (!el) return;
if (playing) el.pause();
else void el.play();
setPlaying(!playing);
}}
>
播放 / 暫停
</button>
</div>
);
}分析麥克風
把 getUserMedia 取得的 MediaStream 傳給 stream(麥克風不會回放,避免回授):
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
<AudioVisualizer variant="radial" stream={stream} sensitivity={1.4} />Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
variant | "bars" | "mirror" | "radial" | "bars" | 視覺化款式:底部長條、中線鏡像、圓形徑向 |
media | RefObject<HTMLMediaElement | null> | — | 要分析的 <audio>/<video> ref;提供後分析真實頻譜 |
stream | MediaStream | null | null | 直接提供音訊串流(例如麥克風);優先於 media |
active | boolean | true | 是否運行動畫;false 時凍結為靜止低振幅狀態 |
bars | number | 48 | 頻段數量(radial 為射線數),內部夾在 4–256 |
color | string | 隨主題的 neutral 色 | 主色;未指定時跟隨 currentColor(隨深淺主題切換) |
fftSize | number | 2048 | FFT 視窗大小,需為 2 的次方(32–32768) |
smoothing | number | 0.8 | 頻譜平滑係數 0–1,越大越平順、反應越慢 |
sensitivity | number | 1 | 靈敏度倍率,放大整體振幅 |
label | string | "音訊視覺化" | 套用於容器 aria-label 的無障礙標籤 |
className | string | — | 附加到最外層容器的 class |
細節
- 雙模式資料來源。 提供
media或stream時建立AnalyserNode,每幀以getByteFrequencyData讀取真實頻譜,並依「近似對數」分組為bars條、強調低頻;未提供音源時退回程序化合成資料(多層正弦振盪 + 節拍脈衝),示範環境無需外部音檔即可展示 - canvas 2D 單層繪製。 三種款式都畫在同一張
<canvas>:bars由底部升起、mirror以中線上下鏡像、radial沿圓周向外輻射;每幀clearRect後重繪,圓角以原生roundRect(不支援時退化為arcTo手繪) - MediaElementSource 只能建立一次。 同一個媒體元素在整個生命週期僅能建立一次來源節點,重複建立會拋錯;元件以
WeakMap快取「context / source / analyser」音訊圖,重掛載或重渲染時重用,元素被回收後自動釋放 - AudioContext 生命週期。 由麥克風
stream建立的 context 會在卸載時close();媒體元素的音訊圖因被快取重用故不關閉。context 若處於suspended(瀏覽器需使用者手勢),在active時嘗試resume() - 以
devicePixelRatio(上限 2)設定實際畫布尺寸再用setTransform縮放,Retina 螢幕維持銳利;ResizeObserver監看容器變化即時重繪 - 未指定
color時讀取 canvas 的currentColor(由text-neutral-*class 解析),並以MutationObserver監看<html>的 class 變化,深淺主題切換時即時重讀顏色
可及性
- 容器帶
role="img"與可自訂的aria-label(預設「音訊視覺化」),內部<canvas>標記aria-hidden,不朗讀逐格畫面 - 使用者系統開啟「減少動態效果」時,
requestAnimationFrame迴圈完全停用,只渲染一張靜止的低振幅剪影,DOM 結構不變(毋須兩段式掛載) - 視覺化為使用者已聽見之聲音的裝飾性映射;請確保實際的播放控制(播放/暫停按鈕等)本身具備完整的鍵盤與標籤支援