这个示例使用了 Bootstrap 5 来快速构建美观的UI界面,并使用原生 JavaScript 来实现所有交互逻辑。

(图片来源网络,侵删)
最终效果预览
这是一个静态图片,展示了播放器在网页中的样子:
完整源代码
您可以将以下所有代码复制到一个 .html 文件中,然后用浏览器打开即可运行。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">网页音乐播放器</title>
<!-- Bootstrap 5 CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Bootstrap Icons -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.2/font/bootstrap-icons.min.css">
<style>
body {
background-color: #f8f9fa;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
.music-player {
width: 100%;
max-width: 400px;
background-color: #ffffff;
border-radius: 16px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.player-header {
background-color: #343a40;
color: white;
padding: 20px;
text-align: center;
}
.album-art {
width: 200px;
height: 200px;
border-radius: 12px;
margin: 0 auto 20px;
object-fit: cover;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
}
.song-info h3 {
margin: 0;
font-size: 1.25rem;
font-weight: 600;
}
.song-info p {
margin: 5px 0 0;
font-size: 0.9rem;
opacity: 0.8;
}
.player-controls {
padding: 20px;
}
.progress-container {
margin-bottom: 20px;
}
.progress {
height: 6px;
cursor: pointer;
border-radius: 3px;
}
.progress-bar {
background-color: #0d6efd;
}
.time {
display: flex;
justify-content: space-between;
font-size: 0.8rem;
color: #6c757d;
margin-top: 5px;
}
.control-buttons {
display: flex;
justify-content: center;
align-items: center;
gap: 20px;
}
.control-btn {
background: none;
border: none;
color: #343a40;
font-size: 1.5rem;
cursor: pointer;
transition: color 0.2s;
}
.control-btn:hover {
color: #0d6efd;
}
.play-pause-btn {
font-size: 2.5rem;
color: #0d6efd;
}
.play-pause-btn:hover {
color: #0b5ed7;
}
.mode-buttons {
display: flex;
justify-content: center;
gap: 15px;
padding: 0 20px 20px;
}
.mode-btn {
background: none;
border: none;
color: #6c757d;
font-size: 1.2rem;
cursor: pointer;
transition: color 0.2s;
}
.mode-btn.active {
color: #0d6efd;
}
.mode-btn:hover {
color: #0d6efd;
}
.playlist-container {
max-height: 200px;
overflow-y: auto;
}
.playlist-item {
padding: 10px 15px;
border-bottom: 1px solid #e9ecef;
cursor: pointer;
transition: background-color 0.2s;
display: flex;
align-items: center;
gap: 10px;
}
.playlist-item:hover {
background-color: #f8f9fa;
}
.playlist-item.active {
background-color: #e7f1ff;
color: #0d6efd;
font-weight: 500;
}
.playlist-item i {
color: #6c757d;
}
</style>
</head>
<body>
<div class="music-player">
<div class="player-header">
<img src="https://via.placeholder.com/200" alt="Album Art" class="album-art" id="albumArt">
<div class="song-info">
<h3 id="songTitle">歌曲标题</h3>
<p id="artistName">艺术家</p>
</div>
</div>
<div class="player-controls">
<div class="progress-container" id="progressContainer">
<div class="progress" id="progressBar">
<div class="progress-bar" role="progressbar" style="width: 0%"></div>
</div>
<div class="time">
<span id="currentTime">0:00</span>
<span id="duration">0:00</span>
</div>
</div>
<div class="control-buttons">
<button class="control-btn" id="prevBtn"><i class="bi bi-skip-start-fill"></i></button>
<button class="control-btn play-pause-btn" id="playPauseBtn"><i class="bi bi-play-fill"></i></button>
<button class="control-btn" id="nextBtn"><i class="bi bi-skip-end-fill"></i></button>
</div>
</div>
<div class="mode-buttons">
<button class="mode-btn" id="shuffleBtn" title="随机播放">
<i class="bi bi-shuffle"></i>
</button>
<button class="mode-btn active" id="repeatBtn" title="循环播放">
<i class="bi bi-arrow-repeat"></i>
</button>
</div>
<div class="playlist-container">
<div class="playlist-item active" data-index="0">
<i class="bi bi-music-note-beamed"></i>
<span>夜曲</span>
</div>
<div class="playlist-item" data-index="1">
<i class="bi bi-music-note-beamed"></i>
<span>晴天</span>
</div>
<div class="playlist-item" data-index="2">
<i class="bi bi-music-note-beamed"></i>
<span>七里香</span>
</div>
<div class="playlist-item" data-index="3">
<i class="bi bi-music-note-beamed"></i>
<span>稻香</span>
</div>
</div>
</div>
<!-- Audio Element -->
<audio id="audioPlayer"></audio>
<!-- Bootstrap JS (not strictly needed for this example, but good practice) -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script>
// 获取DOM元素
const audioPlayer = document.getElementById('audioPlayer');
const playPauseBtn = document.getElementById('playPauseBtn');
const prevBtn = document.getElementById('prevBtn');
const nextBtn = document.getElementById('nextBtn');
const progressBar = document.getElementById('progressBar');
const progressContainer = document.getElementById('progressContainer');
const currentTimeEl = document.getElementById('currentTime');
const durationEl = document.getElementById('duration');
const songTitleEl = document.getElementById('songTitle');
const artistNameEl = document.getElementById('artistName');
const albumArtEl = document.getElementById('albumArt');
const shuffleBtn = document.getElementById('shuffleBtn');
const repeatBtn = document.getElementById('repeatBtn');
const playlistItems = document.querySelectorAll('.playlist-item');
// 播放列表数据
const playlist = [
{ src: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3', title: '夜曲', artist: '周杰伦', art: 'https://via.placeholder.com/200/343a40/FFFFFF?text=Song+1' },
{ src: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3', title: '晴天', artist: '周杰伦', art: 'https://via.placeholder.com/200/007bff/FFFFFF?text=Song+2' },
{ src: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-3.mp3', title: '七里香', artist: '周杰伦', art: 'https://via.placeholder.com/200/28a745/FFFFFF?text=Song+3' },
{ src: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-4.mp3', title: '稻香', artist: '周杰伦', art: 'https://via.placeholder.com/200/dc3545/FFFFFF?text=Song+4' }
];
let currentSongIndex = 0;
let isShuffling = false;
let isRepeating = true;
// 初始化
loadSong(currentSongIndex);
// 加载歌曲
function loadSong(index) {
const song = playlist[index];
audioPlayer.src = song.src;
songTitleEl.textContent = song.title;
artistNameEl.textContent = song.artist;
albumArtEl.src = song.art;
// 更新播放列表高亮
playlistItems.forEach((item, i) => {
item.classList.toggle('active', i === index);
});
// 如果音频已经加载了元数据,则更新时长
audioPlayer.addEventListener('loadedmetadata', updateDuration);
}
// 播放/暂停
function togglePlayPause() {
if (audioPlayer.paused) {
audioPlayer.play();
playPauseBtn.innerHTML = '<i class="bi bi-pause-fill"></i>';
} else {
audioPlayer.pause();
playPauseBtn.innerHTML = '<i class="bi bi-play-fill"></i>';
}
}
// 上一首
function prevSong() {
if (isShuffling) {
currentSongIndex = Math.floor(Math.random() * playlist.length);
} else {
currentSongIndex--;
if (currentSongIndex < 0) {
currentSongIndex = playlist.length - 1;
}
}
loadSong(currentSongIndex);
audioPlayer.play();
playPauseBtn.innerHTML = '<i class="bi bi-pause-fill"></i>';
}
// 下一首
function nextSong() {
if (isShuffling) {
currentSongIndex = Math.floor(Math.random() * playlist.length);
} else {
currentSongIndex++;
if (currentSongIndex >= playlist.length) {
if (isRepeating) {
currentSongIndex = 0;
} else {
// 如果不是循环模式,则停在最后一首
audioPlayer.pause();
playPauseBtn.innerHTML = '<i class="bi bi-play-fill"></i>';
return;
}
}
}
loadSong(currentSongIndex);
audioPlayer.play();
playPauseBtn.innerHTML = '<i class="bi bi-pause-fill"></i>';
}
// 更新进度条
function updateProgress() {
const { currentTime, duration } = audioPlayer;
const progressPercent = (currentTime / duration) * 100;
progressBar.querySelector('.progress-bar').style.width = `${progressPercent}%`;
currentTimeEl.textContent = formatTime(currentTime);
}
// 设置进度条
function setProgress(e) {
const width = this.clientWidth;
const clickX = e.offsetX;
const duration = audioPlayer.duration;
audioPlayer.currentTime = (clickX / width) * duration;
}
// 更新总时长
function updateDuration() {
durationEl.textContent = formatTime(audioPlayer.duration);
}
// 格式化时间 (秒 -> mm:ss)
function formatTime(seconds) {
if (isNaN(seconds)) return '0:00';
const minutes = Math.floor(seconds / 60);
const remainingSeconds = Math.floor(seconds % 60);
return `${minutes}:${remainingSeconds < 10 ? '0' : ''}${remainingSeconds}`;
}
// 切换随机播放
function toggleShuffle() {
isShuffling = !isShuffling;
shuffleBtn.classList.toggle('active', isShuffling);
}
// 切换循环播放
function toggleRepeat() {
isRepeating = !isRepeating;
repeatBtn.classList.toggle('active', isRepeating);
}
// 从播放列表选择歌曲
function selectSongFromPlaylist(index) {
currentSongIndex = index;
loadSong(currentSongIndex);
audioPlayer.play();
playPauseBtn.innerHTML = '<i class="bi bi-pause-fill"></i>';
}
// 事件监听器
playPauseBtn.addEventListener('click', togglePlayPause);
prevBtn.addEventListener('click', prevSong);
nextBtn.addEventListener('click', nextSong);
audioPlayer.addEventListener('timeupdate', updateProgress);
audioPlayer.addEventListener('ended', nextSong); // 歌曲结束时自动播放下一首
progressContainer.addEventListener('click', setProgress);
shuffleBtn.addEventListener('click', toggleShuffle);
repeatBtn.addEventListener('click', toggleRepeat);
playlistItems.forEach(item => {
item.addEventListener('click', () => {
const index = parseInt(item.getAttribute('data-index'));
selectSongFromPlaylist(index);
});
});
</script>
</body>
</html>
代码结构解析
HTML 结构
<audio>元素: 这是核心,用于加载和播放音频文件,它被放置在页面中,但默认是隐藏的。- 播放器容器: 一个
div包含了所有UI元素,如封面、歌曲信息、控制按钮等。 - 控制按钮: 使用了 Bootstrap Icons 来提供清晰的图标。
- 进度条: 一个可点击的
div,用于显示和调整播放进度。 - 播放列表: 一个简单的列表,每一项都可以点击来切换歌曲。
CSS 样式
- 使用了 Bootstrap 5 的网格和工具类来快速布局。
- 自定义样式用于美化播放器,如圆角、阴影、颜色过渡等,使其看起来更现代。
.playlist-item.active类用于高亮当前正在播放的歌曲。
JavaScript 逻辑
这是播放器的“大脑”,实现了所有交互功能:
-
playlist数组: 存储了所有歌曲的信息,包括音频URL、标题、艺术家和封面图。注意:示例中的音频URL是公开的测试音频,你可以替换成你自己的音频文件路径(建议使用相对路径或可靠的CDN链接)。
(图片来源网络,侵删) -
loadSong(index)函数:- 根据
index从playlist数组中获取歌曲信息。 - 将歌曲信息更新到UI上(标题、艺术家、封面)。
- 更新播放列表中当前歌曲的高亮状态。
- 根据
-
togglePlayPause()函数:- 检查
audioPlayer的paused属性。 - 如果是暂停状态,则调用
play()方法并切换图标为“暂停”。 - 如果是播放状态,则调用
pause()方法并切换图标为“播放”。
- 检查
-
prevSong()和nextSong()函数:- 根据
isShuffling(是否随机)标志来决定如何计算下一首歌的索引。 - 处理边界情况(第一首歌的上一首是最后一首,最后一首歌的下一首是第一首)。
- 加载新歌曲并自动播放。
- 根据
-
updateProgress()函数:- 通过
timeupdate事件持续监听音频的播放进度。 - 计算当前播放时间占总时长的百分比,并更新进度条的宽度。
- 调用
formatTime函数来格式化并显示当前时间和总时长。
- 通过
-
setProgress(e)函数:- 当用户点击进度条时,计算点击位置相对于总宽度的比例。
- 根据这个比例设置
audioPlayer的currentTime,实现跳转播放。
-
事件监听器:
- 为所有按钮、进度条和播放列表项绑定了点击事件,调用相应的功能函数。
- 为
audioPlayer绑定了timeupdate和ended事件,分别用于更新进度和实现“自动播放下一首”功能。
如何使用和修改
- 保存文件: 将所有代码复制并粘贴到一个新文件中,命名为
music-player.html。 - 替换音频: 修改
playlist数组中的src属性,将其替换为你自己的音频文件路径。src: "songs/my-song.mp3"。 - 替换封面: 修改
playlist数组中的art属性,替换为你自己的图片URL或本地路径。 - 修改歌单: 在
playlist数组中添加或删除歌曲对象,并相应地更新HTML中的播放列表项。 - 自定义样式: 你可以自由修改
<style>标签中的CSS代码来改变播放器的颜色、大小和布局。
