以下是一个完整的前端音乐播放器综合项目,使用纯 HTML + CSS + JavaScript 实现(无任何框架),功能较为全面,适合作为大项目练习或简历展示。
项目功能
- 播放 / 暂停
- 上一首 / 下一首
- 进度条(可拖动跳转)
- 音量控制(可拖动)
- 随机播放 / 循环模式切换
- 歌单展示 & 点击切歌
- 歌曲封面旋转动画
- 响应式布局(手机 / 平板 / 桌面适配)
- 时间显示(当前时间 / 总时长)
- 歌曲信息动态切换
完整代码(单文件版,易复制运行)
将下面代码保存为 index.html,然后准备一个 music/ 文件夹放 mp3 文件和封面图,即可直接在浏览器打开运行。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>极简音乐播放器</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css"/>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
color: white;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.player {
background: rgba(255, 255, 255, 0.12);
backdrop-filter: blur(10px);
border-radius: 24px;
padding: 32px;
width: 100%;
max-width: 420px;
box-shadow: 0 20px 40px rgba(0,0,0,0.4);
text-align: center;
}
.cover {
width: 220px;
height: 220px;
margin: 0 auto 24px;
border-radius: 16px;
overflow: hidden;
box-shadow: 0 15px 35px rgba(0,0,0,0.5);
position: relative;
}
.cover img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.6s ease;
}
.cover.playing img {
animation: rotate 20s linear infinite;
}
@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
h2 {
font-size: 1.6rem;
margin-bottom: 8px;
font-weight: 600;
}
.artist {
font-size: 1rem;
opacity: 0.8;
margin-bottom: 28px;
}
.progress-container {
height: 6px;
background: rgba(255,255,255,0.2);
border-radius: 10px;
margin: 20px 0 10px;
cursor: pointer;
position: relative;
}
.progress {
background: #fff;
width: 0%;
height: 100%;
border-radius: 10px;
transition: width 0.1s linear;
}
.time {
display: flex;
justify-content: space-between;
font-size: 0.85rem;
opacity: 0.9;
margin-bottom: 20px;
}
.controls {
display: flex;
justify-content: center;
align-items: center;
gap: 20px;
margin: 20px 0 30px;
}
.btn {
background: rgba(255,255,255,0.15);
border: none;
color: white;
font-size: 1.4rem;
width: 50px;
height: 50px;
border-radius: 50%;
cursor: pointer;
transition: all 0.3s;
display: flex;
align-items: center;
justify-content: center;
}
.btn:hover {
background: rgba(255,255,255,0.3);
transform: scale(1.1);
}
.btn.play-pause {
font-size: 1.8rem;
width: 70px;
height: 70px;
background: rgba(255,255,255,0.25);
}
.volume-container {
display: flex;
align-items: center;
gap: 12px;
margin-top: 20px;
}
.volume-bar {
flex: 1;
height: 6px;
background: rgba(255,255,255,0.2);
border-radius: 10px;
cursor: pointer;
position: relative;
}
.volume-progress {
background: #fff;
width: 70%;
height: 100%;
border-radius: 10px;
}
.playlist {
margin-top: 32px;
max-height: 240px;
overflow-y: auto;
}
.song-item {
display: flex;
align-items: center;
padding: 12px;
border-radius: 12px;
margin-bottom: 8px;
cursor: pointer;
transition: all 0.2s;
}
.song-item:hover,
.song-item.active {
background: rgba(255,255,255,0.18);
}
.song-item img {
width: 50px;
height: 50px;
border-radius: 8px;
margin-right: 16px;
object-fit: cover;
}
.song-info {
text-align: left;
flex: 1;
}
.song-title {
font-weight: 500;
}
.song-artist {
font-size: 0.85rem;
opacity: 0.8;
}
/* 滚动条美化 */
.playlist::-webkit-scrollbar {
width: 6px;
}
.playlist::-webkit-scrollbar-thumb {
background: rgba(255,255,255,0.4);
border-radius: 10px;
}
</style>
</head>
<body>
<div class="player">
<div class="cover" id="cover">
<img id="coverImg" src="https://images.unsplash.com/photo-1611339555312-e607c8352fd7?w=500" alt="cover">
</div>
<h2 id="title">歌曲标题</h2>
<p class="artist" id="artist">歌手</p>
<div class="progress-container" id="progressContainer">
<div class="progress" id="progress"></div>
</div>
<div class="time">
<span id="currentTime">0:00</span>
<span id="duration">0:00</span>
</div>
<div class="controls">
<button class="btn" id="shuffleBtn" title="随机播放"><i class="fas fa-random"></i></button>
<button class="btn" id="prev"><i class="fas fa-backward"></i></button>
<button class="btn play-pause" id="play"><i class="fas fa-play"></i></button>
<button class="btn" id="next"><i class="fas fa-forward"></i></button>
<button class="btn" id="repeatBtn" title="循环"><i class="fas fa-redo"></i></button>
</div>
<div class="volume-container">
<i class="fas fa-volume-down"></i>
<div class="volume-bar" id="volumeBar">
<div class="volume-progress" id="volumeProgress"></div>
</div>
</div>
<div class="playlist" id="playlist">
<!-- 歌单动态生成 -->
</div>
</div>
<audio id="audio"></audio>
<script>
const audio = document.getElementById('audio');
const playBtn = document.getElementById('play');
const prevBtn = document.getElementById('prev');
const nextBtn = document.getElementById('next');
const titleEl = document.getElementById('title');
const artistEl = document.getElementById('artist');
const coverImg = document.getElementById('coverImg');
const progressContainer = document.getElementById('progressContainer');
const progress = document.getElementById('progress');
const currentTimeEl = document.getElementById('currentTime');
const durationEl = document.getElementById('duration');
const volumeBar = document.getElementById('volumeBar');
const volumeProgress = document.getElementById('volumeProgress');
const playlistEl = document.getElementById('playlist');
const cover = document.getElementById('cover');
const shuffleBtn = document.getElementById('shuffleBtn');
const repeatBtn = document.getElementById('repeatBtn');
// 歌曲数据(可自行扩展)
const songs = [
{
title: "Despacito",
artist: "Luis Fonsi ft. Daddy Yankee",
src: "music/despacito.mp3",
cover: "https://images.unsplash.com/photo-1611339555312-e607c8352fd7?w=300"
},
{
title: "Shape of You",
artist: "Ed Sheeran",
src: "music/shape-of-you.mp3",
cover: "https://images.unsplash.com/photo-1493225454679-181216a3b5b0?w=300"
},
{
title: "Blinding Lights",
artist: "The Weeknd",
src: "music/blinding-lights.mp3",
cover: "https://images.unsplash.com/photo-1557672172-298e090bd0f1?w=300"
},
// 添加更多歌曲...
];
let currentSong = 0;
let isPlaying = false;
let isShuffle = false;
let repeatMode = 'none'; // none / one / all
function loadSong(index) {
const song = songs[index];
titleEl.textContent = song.title;
artistEl.textContent = song.artist;
audio.src = song.src;
coverImg.src = song.cover;
// 高亮当前歌曲
document.querySelectorAll('.song-item').forEach((el,i) => {
el.classList.toggle('active', i === index);
});
}
function playSong() {
audio.play().catch(e => console.log("播放失败", e));
playBtn.querySelector('i').classList.replace('fa-play', 'fa-pause');
cover.classList.add('playing');
isPlaying = true;
}
function pauseSong() {
audio.pause();
playBtn.querySelector('i').classList.replace('fa-pause', 'fa-play');
cover.classList.remove('playing');
isPlaying = false;
}
function togglePlay() {
isPlaying ? pauseSong() : playSong();
}
function prevSong() {
currentSong = (currentSong - 1 + songs.length) % songs.length;
loadSong(currentSong);
playSong();
}
function nextSong() {
if (isShuffle) {
let random = Math.floor(Math.random() * songs.length);
while (random === currentSong) random = Math.floor(Math.random() * songs.length);
currentSong = random;
} else {
currentSong = (currentSong + 1) % songs.length;
}
loadSong(currentSong);
playSong();
}
// 更新进度条
audio.addEventListener('timeupdate', () => {
if (!audio.duration) return;
const percent = (audio.currentTime / audio.duration) * 100;
progress.style.width = percent + '%';
currentTimeEl.textContent = formatTime(audio.currentTime);
durationEl.textContent = formatTime(audio.duration);
});
function formatTime(seconds) {
const min = Math.floor(seconds / 60);
const sec = Math.floor(seconds % 60);
return `${min}:${sec < 10 ? '0' : ''}${sec}`;
}
// 点击进度条跳转
progressContainer.addEventListener('click', e => {
const width = progressContainer.clientWidth;
const clickX = e.offsetX;
audio.currentTime = (clickX / width) * audio.duration;
});
// 音量控制
volumeBar.addEventListener('click', e => {
const width = volumeBar.clientWidth;
const clickX = e.offsetX;
const vol = clickX / width;
audio.volume = vol;
volumeProgress.style.width = vol * 100 + '%';
});
// 自动下一首
audio.addEventListener('ended', () => {
if (repeatMode === 'one') {
audio.currentTime = 0;
playSong();
} else if (repeatMode === 'all' || isShuffle) {
nextSong();
} else {
if (currentSong < songs.length - 1) nextSong();
}
});
// 歌单渲染
function renderPlaylist() {
playlistEl.innerHTML = '';
songs.forEach((song, i) => {
const item = document.createElement('div');
item.classList.add('song-item');
if (i === currentSong) item.classList.add('active');
item.innerHTML = `
<img src="${song.cover}" alt="">
<div class="song-info">
<div class="song-title">${song.title}</div>
<div class="song-artist">${song.artist}</div>
</div>
`;
item.addEventListener('click', () => {
currentSong = i;
loadSong(i);
playSong();
});
playlistEl.appendChild(item);
});
}
// 模式切换
shuffleBtn.addEventListener('click', () => {
isShuffle = !isShuffle;
shuffleBtn.style.color = isShuffle ? '#00ff9d' : 'white';
});
repeatBtn.addEventListener('click', () => {
if (repeatMode === 'none') {
repeatMode = 'all';
repeatBtn.innerHTML = '<i class="fas fa-redo"></i>';
repeatBtn.title = '列表循环';
} else if (repeatMode === 'all') {
repeatMode = 'one';
repeatBtn.innerHTML = '<i class="fas fa-redo-alt"></i>';
repeatBtn.title = '单曲循环';
} else {
repeatMode = 'none';
repeatBtn.innerHTML = '<i class="fas fa-redo"></i>';
repeatBtn.title = '不循环';
}
});
// 事件绑定
playBtn.addEventListener('click', togglePlay);
prevBtn.addEventListener('click', prevSong);
nextBtn.addEventListener('click', nextSong);
// 初始化
loadSong(currentSong);
renderPlaylist();
audio.volume = 0.7;
volumeProgress.style.width = '70%';
</script>
</body>
</html>
如何扩展这个项目(进阶方向)
- 本地文件上传:使用
<input type="file" multiple>+ File API 读取本地音乐 - 歌词同步:解析 LRC 文件,结合 timeupdate 事件显示
- 均衡器:使用 Web Audio API 实现简单 equalizer
- 播放列表管理:支持拖拽排序、增删歌曲(localStorage 保存)
- 主题切换:明暗模式、自定义背景
- PWA 支持:添加 manifest 和 service worker 实现离线播放
- 搜索功能:在歌单中实时过滤歌曲
小建议
- 准备 3–5 首 mp3 + 对应封面图,放在
music/文件夹 - 替换 songs 数组里的 src 和 cover 路径
- 想更美观?可以参考 neumorphism、glassmorphism 或 3D 卡片风格
祝你完成一个漂亮的音乐播放器项目!如果需要添加某个具体功能(歌词、拖拽排序、夜间模式等),可以告诉我,我帮你继续完善代码。