如何使用 Claude Code 构建音频播放器
学习如何使用 Claude Code 构建音频播放器。包含实用代码示例和分步指导。
使用 Claude Code 构建音频播放器
音乐流媒体、播客、教育内容等场景中经常需要自定义音频播放器。借助 Claude Code,你可以构建带有波形显示和播放列表功能的专业级播放器。
基础播放器实现
> 创建一个自定义音频播放器。
> 支持播放/暂停、进度条、音量控制和播放速度调节。
> 还要实现播放列表功能和波形显示。
// src/components/AudioPlayer.tsx
'use client';
import { useRef, useState, useEffect } from 'react';
interface Track {
id: string;
title: string;
artist: string;
src: string;
duration: number;
coverArt?: string;
}
interface AudioPlayerProps {
tracks: Track[];
initialTrackIndex?: number;
}
export function AudioPlayer({ tracks, initialTrackIndex = 0 }: AudioPlayerProps) {
const audioRef = useRef<HTMLAudioElement>(null);
const [currentTrackIndex, setCurrentTrackIndex] = useState(initialTrackIndex);
const [isPlaying, setIsPlaying] = useState(false);
const [currentTime, setCurrentTime] = useState(0);
const [duration, setDuration] = useState(0);
const [volume, setVolume] = useState(0.8);
const currentTrack = tracks[currentTrackIndex];
useEffect(() => {
const audio = audioRef.current;
if (!audio) return;
const onTimeUpdate = () => setCurrentTime(audio.currentTime);
const onLoadedMetadata = () => setDuration(audio.duration);
const onEnded = () => playNext();
audio.addEventListener('timeupdate', onTimeUpdate);
audio.addEventListener('loadedmetadata', onLoadedMetadata);
audio.addEventListener('ended', onEnded);
return () => {
audio.removeEventListener('timeupdate', onTimeUpdate);
audio.removeEventListener('loadedmetadata', onLoadedMetadata);
audio.removeEventListener('ended', onEnded);
};
}, [currentTrackIndex]);
const togglePlay = () => {
const audio = audioRef.current;
if (!audio) return;
if (audio.paused) {
audio.play();
setIsPlaying(true);
} else {
audio.pause();
setIsPlaying(false);
}
};
const playNext = () => {
const nextIndex = (currentTrackIndex + 1) % tracks.length;
setCurrentTrackIndex(nextIndex);
setTimeout(() => {
audioRef.current?.play();
setIsPlaying(true);
}, 100);
};
const playPrevious = () => {
if (currentTime > 3) {
// 超过3秒则回到开头
audioRef.current!.currentTime = 0;
} else {
const prevIndex = (currentTrackIndex - 1 + tracks.length) % tracks.length;
setCurrentTrackIndex(prevIndex);
setTimeout(() => {
audioRef.current?.play();
setIsPlaying(true);
}, 100);
}
};
const formatTime = (sec: number) => {
const m = Math.floor(sec / 60);
const s = Math.floor(sec % 60);
return `${m}:${s.toString().padStart(2, '0')}`;
};
return (
<div className="bg-white dark:bg-gray-800 rounded-2xl shadow-lg overflow-hidden max-w-md mx-auto">
<audio ref={audioRef} src={currentTrack.src} preload="metadata" />
{/* 封面 */}
<div className="aspect-square bg-gray-200 dark:bg-gray-700 relative">
{currentTrack.coverArt ? (
<img src={currentTrack.coverArt} alt={currentTrack.title} className="w-full h-full object-cover" />
) : (
<div className="w-full h-full flex items-center justify-center text-6xl text-gray-400">♪</div>
)}
</div>
{/* 曲目信息 */}
<div className="p-6">
<h3 className="text-lg font-bold dark:text-white truncate">{currentTrack.title}</h3>
<p className="text-gray-500 dark:text-gray-400 text-sm">{currentTrack.artist}</p>
{/* 进度条 */}
<div className="mt-4">
<input
type="range"
min={0}
max={duration || 0}
value={currentTime}
onChange={(e) => {
const time = Number(e.target.value);
audioRef.current!.currentTime = time;
setCurrentTime(time);
}}
className="w-full h-1 accent-blue-600"
/>
<div className="flex justify-between text-xs text-gray-400 mt-1">
<span>{formatTime(currentTime)}</span>
<span>{formatTime(duration)}</span>
</div>
</div>
{/* 控制按钮 */}
<div className="flex items-center justify-center gap-6 mt-4">
<button onClick={playPrevious} className="text-2xl dark:text-white hover:text-blue-600">⏮</button>
<button
onClick={togglePlay}
className="w-14 h-14 rounded-full bg-blue-600 text-white text-2xl flex items-center justify-center hover:bg-blue-700"
>
{isPlaying ? '⏸' : '▶'}
</button>
<button onClick={playNext} className="text-2xl dark:text-white hover:text-blue-600">⏭</button>
</div>
{/* 音量 */}
<div className="flex items-center gap-2 mt-4">
<span className="text-sm dark:text-gray-400">🔊</span>
<input
type="range"
min={0}
max={1}
step={0.05}
value={volume}
onChange={(e) => {
const vol = Number(e.target.value);
audioRef.current!.volume = vol;
setVolume(vol);
}}
className="flex-1 h-1 accent-blue-600"
/>
</div>
</div>
{/* 播放列表 */}
<div className="border-t dark:border-gray-700 max-h-60 overflow-y-auto">
{tracks.map((track, index) => (
<button
key={track.id}
onClick={() => {
setCurrentTrackIndex(index);
setTimeout(() => { audioRef.current?.play(); setIsPlaying(true); }, 100);
}}
className={`w-full flex items-center gap-3 p-3 text-left hover:bg-gray-50 dark:hover:bg-gray-700 ${
index === currentTrackIndex ? 'bg-blue-50 dark:bg-blue-900/30' : ''
}`}
>
<span className="text-xs text-gray-400 w-6 text-right">
{index === currentTrackIndex && isPlaying ? '♪' : index + 1}
</span>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium dark:text-white truncate">{track.title}</p>
<p className="text-xs text-gray-500 truncate">{track.artist}</p>
</div>
<span className="text-xs text-gray-400">{formatTime(track.duration)}</span>
</button>
))}
</div>
</div>
);
}
使用 Web Audio API 显示波形
作为高级功能,你还可以让 Claude Code 实现基于 Web Audio API 的实时波形显示。通过 AudioContext 和 AnalyserNode 的组合,可以在 Canvas 上绘制播放中的音频波形。
相关文章
视频播放器的实现请参阅视频播放器构建指南,响应式适配请参阅响应式设计。
Web Audio API 的详细信息请参阅 MDN(developer.mozilla.org/Web/API/Web_Audio_API)。
#Claude Code
#audio
#Web Audio API
#React
#player
Related Posts
Use Cases
Use Cases
用 Claude Code 加速个人项目开发【附实战案例】
详解如何用 Claude Code 大幅提升个人项目的开发速度。包含从创意到上线的完整实战案例和工作流。
Use Cases
Use Cases
如何用 Claude Code 自动化代码重构
详解如何利用 Claude Code 高效完成代码重构自动化。包含实用提示词和真实项目中的重构模式。
Use Cases
Use Cases
Complete CORS Configuration Guide:Claude Code 实战指南
了解complete cors configuration guide:Claude Code 实战. 包含实用技巧和代码示例。