概览

我们将创建一个美观且功能齐全的音乐播放器,它将包含以下功能:

  • 播放/暂停
  • 上一曲/下一曲
  • 进度条(可拖动)
  • 当前播放时间/总时长
  • 音量控制
  • 播放列表
  • 显示当前播放歌曲信息(封面、标题、艺术家)

我们将使用 jQuery 来处理 DOM 操作和事件,使用 CSS3 来美化界面,使用 HTML5 的 <audio> 元素 来处理音频。


第 1 步:准备工作 (HTML & CSS)

我们需要一个基本的 HTML 结构和一些 CSS 样式来构建播放器的界面。

1 HTML 结构 (index.html)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">jQuery 音乐播放器</title>
    <link rel="stylesheet" href="style.css">
    <!-- 引入 jQuery -->
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body>
    <div class="music-player">
        <!-- 歌曲信息显示区 -->
        <div class="now-playing">
            <img src="https://via.placeholder.com/150" alt="Album Cover" id="album-cover">
            <div class="song-info">
                <h2 id="song-title">歌曲标题</h2>
                <p id="artist-name">艺术家</p>
            </div>
        </div>
        <!-- 进度条区域 -->
        <div class="progress-container">
            <span id="current-time">0:00</span>
            <div class="progress-bar">
                <div class="progress"></div>
            </div>
            <span id="duration">0:00</span>
        </div>
        <!-- 控制按钮 -->
        <div class="controls">
            <button id="prev-btn" class="control-btn">
                <i class="fas fa-step-backward"></i>
            </button>
            <button id="play-pause-btn" class="control-btn play-pause">
                <i class="fas fa-play"></i>
            </button>
            <button id="next-btn" class="control-btn">
                <i class="fas fa-step-forward"></i>
            </button>
        </div>
        <!-- 音量控制 -->
        <div class="volume-container">
            <i class="fas fa-volume-up"></i>
            <input type="range" id="volume-slider" min="0" max="1" step="0.01" value="1">
        </div>
        <!-- 播放列表 -->
        <div class="playlist">
            <h3>播放列表</h3>
            <ul id="playlist-songs">
                <!-- 播放列表项将通过 jQuery 动态生成 -->
            </ul>
        </div>
    </div>
    <!-- 隐藏的 audio 元素,用于实际播放 -->
    <audio id="audio-player"></audio>
    <!-- 引入 Font Awesome 图标库 -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
    <!-- 引入我们的 JavaScript 文件 -->
    <script src="script.js"></script>
</body>
</html>

2 CSS 样式 (style.css)

为了让播放器看起来更美观,我们需要添加一些样式。

body {
    font-family: 'Arial', sans-serif;
    background-color: #f0f2f5;
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
    margin: 0;
}
.music-player {
    background-color: #fff;
    border-radius: 15px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
    width: 400px;
    padding: 25px;
    text-align: center;
}
.now-playing {
    display: flex;
    align-items: center;
    margin-bottom: 20px;
}
#album-cover {
    width: 100px;
    height: 100px;
    border-radius: 10px;
    margin-right: 20px;
}
.song-info {
    text-align: left;
}
.song-info h2 {
    margin: 0 0 5px 0;
    font-size: 1.2em;
}
.song-info p {
    margin: 0;
    color: #666;
}
.progress-container {
    display: flex;
    align-items: center;
    margin-bottom: 20px;
}
.progress-bar {
    flex-grow: 1;
    height: 6px;
    background-color: #e0e0e0;
    border-radius: 3px;
    cursor: pointer;
    position: relative;
}
.progress {
    height: 100%;
    background-color: #4CAF50;
    border-radius: 3px;
    width: 0%;
}
.controls {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 20px;
    margin-bottom: 20px;
}
.control-btn {
    background: none;
    border: none;
    font-size: 1.5em;
    color: #333;
    cursor: pointer;
    transition: color 0.2s;
}
.control-btn:hover {
    color: #4CAF50;
}
.control-btn.play-pause {
    font-size: 2em;
}
.volume-container {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    margin-bottom: 20px;
}
#volume-slider {
    width: 100px;
}
.playlist {
    text-align: left;
    background-color: #f9f9f9;
    border-radius: 10px;
    padding: 15px;
}
.playlist h3 {
    margin-top: 0;
    margin-bottom: 15px;
    text-align: center;
}
#playlist-songs {
    list-style: none;
    padding: 0;
    margin: 0;
}
#playlist-songs li {
    padding: 10px;
    border-bottom: 1px solid #ddd;
    cursor: pointer;
    transition: background-color 0.2s;
}
#playlist-songs li:hover {
    background-color: #e9e9e9;
}
#playlist-songs li.active {
    background-color: #e0f2f1;
    font-weight: bold;
}

第 2 步:核心 JavaScript 逻辑 (script.js)

这是实现所有功能的核心部分,我们将在这里使用 jQuery 来操作 DOM 和处理事件。

$(document).ready(function() {
    // 1. 获取 DOM 元素
    const audio = $('#audio-player')[0]; // jQuery 对象转换为原生 DOM 元素以使用原生 API
    const playPauseBtn = $('#play-pause-btn');
    const prevBtn = $('#prev-btn');
    const nextBtn = $('#next-btn');
    const progressBar = $('.progress');
    const progressContainer = $('.progress-bar');
    const currentTimeEl = $('#current-time');
    const durationEl = $('#duration');
    const volumeSlider = $('#volume-slider');
    const albumCover = $('#album-cover');
    const songTitle = $('#song-title');
    const artistName = $('#artist-name');
    const playlistSongs = $('#playlist-songs');
    // 2. 播放列表数据
    const playlist = [
        {
            title: "晴天",
            artist: "周杰伦",
            src: "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3",
            cover: "https://via.placeholder.com/150?text=晴天"
        },
        {
            title: "七里香",
            artist: "周杰伦",
            src: "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3",
            cover: "https://via.placeholder.com/150?text=七里香"
        },
        {
            title: "稻香",
            artist: "周杰伦",
            src: "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-3.mp3",
            cover: "https://via.placeholder.com/150?text=稻香"
        }
    ];
    let currentSongIndex = 0;
    // 3. 初始化播放列表
    function initPlaylist() {
        playlistSongs.empty();
        playlist.forEach((song, index) => {
            const li = $('<li>').text(`${song.title} - ${song.artist}`).data('index', index);
            if (index === currentSongIndex) {
                li.addClass('active');
            }
            playlistSongs.append(li);
        });
    }
    // 4. 加载歌曲
    function loadSong(index) {
        const song = playlist[index];
        audio.src = song.src;
        songTitle.text(song.title);
        artistName.text(song.artist);
        albumCover.attr('src', song.cover);
        // 更新播放列表高亮
        playlistSongs.find('li').removeClass('active');
        playlistSongs.find(`li[data-index="${index}"]`).addClass('active');
        // 如果当前是播放状态,则自动播放新歌曲
        if (!audio.paused) {
            audio.play();
        }
    }
    // 5. 播放/暂停功能
    function togglePlayPause() {
        if (audio.paused) {
            audio.play();
            playPauseBtn.html('<i class="fas fa-pause"></i>');
        } else {
            audio.pause();
            playPauseBtn.html('<i class="fas fa-play"></i>');
        }
    }
    // 6. 格式化时间 (秒转 MM:SS)
    function formatTime(seconds) {
        const minutes = Math.floor(seconds / 60);
        const remainingSeconds = Math.floor(seconds % 60);
        return `${minutes}:${remainingSeconds < 10 ? '0' : ''}${remainingSeconds}`;
    }
    // 7. 更新进度条
    function updateProgress() {
        const { duration, currentTime } = audio;
        if (!isNaN(duration)) {
            const progressPercent = (currentTime / duration) * 100;
            progressBar.css('width', `${progressPercent}%`);
            currentTimeEl.text(formatTime(currentTime));
            durationEl.text(formatTime(duration));
        }
    }
    // 8. 设置进度
    function setProgress(e) {
        const width = progressContainer.width();
        const clickX = e.offsetX;
        const duration = audio.duration;
        audio.currentTime = (clickX / width) * duration;
    }
    // 9. 上一曲
    function prevSong() {
        currentSongIndex--;
        if (currentSongIndex < 0) {
            currentSongIndex = playlist.length - 1;
        }
        loadSong(currentSongIndex);
    }
    // 10. 下一曲
    function nextSong() {
        currentSongIndex++;
        if (currentSongIndex >= playlist.length) {
            currentSongIndex = 0;
        }
        loadSong(currentSongIndex);
    }
    // 11. 事件监听器
    playPauseBtn.on('click', togglePlayPause);
    prevBtn.on('click', prevSong);
    nextBtn.on('click', nextSong);
    progressContainer.on('click', setProgress);
    // 音量控制
    volumeSlider.on('input', function() {
        audio.volume = $(this).val();
    });
    // 播放列表点击事件
    playlistSongs.on('click', 'li', function() {
        currentSongIndex = $(this).data('index');
        loadSong(currentSongIndex);
        audio.play();
        playPauseBtn.html('<i class="fas fa-pause"></i>');
    });
    // audio 元素的事件
    audio.addEventListener('timeupdate', updateProgress);
    audio.addEventListener('ended', nextSong); // 歌曲结束时自动播放下一曲
    // 初始化
    initPlaylist();
    loadSong(currentSongIndex);
});

第 3 步:如何运行

  1. 创建文件:将上面的代码分别保存为 index.htmlstyle.cssscript.js 三个文件,并放在同一个文件夹下。
  2. 放置音乐文件:将你的音乐文件(如 .mp3)放在项目文件夹中,并修改 playlist 数组中的 src 属性为你的音乐文件路径(src: "my_music/song1.mp3"),或者,你可以使用我提供的在线示例链接。
  3. 打开网页:用浏览器打开 index.html 文件,你就可以看到一个功能完整的音乐播放器了!

代码解析与最佳实践

  1. jQuery vs. 原生 JS

    • 我们混合使用了 jQuery 和原生 JavaScript,对于选择元素、绑定事件、修改 CSS 等操作,jQuery 非常简洁。
    • HTML5 的 <audio> 元素本身提供了一套原生 API(如 audio.play(), audio.pause(), audio.currentTime),在需要直接操作这些 API 时,将 jQuery 对象(如 $('#audio-player'))转换为原生 DOM 元素($('#audio-player')[0])是更直接高效的方式。
  2. 事件委托

    • playlistSongs.on('click', 'li', ...) 这一行,我们使用了事件委托,这是处理动态生成元素的最佳实践,即使 <li> 是后来通过 jQuery 动态添加的,这个事件监听器依然能正确工作。
  3. 关注点分离

    • HTML 负责结构。
    • CSS 负责样式。
    • JavaScript (jQuery) 负责行为和逻辑。
    • 这种分离让代码更易于维护和理解。
  4. 可扩展性

    • 播放列表数据被存储在一个 JavaScript 数组中,如果你想从服务器加载歌曲列表,你只需要用 AJAX (jQuery 的 $.ajax$.get) 请求一个 JSON 数据,然后用返回的数据替换掉本地的 playlist 数组即可,其他逻辑几乎不需要改动。

这个例子为你提供了一个坚实的基础,你可以基于它继续添加更多高级功能,比如随机播放、循环模式、歌词显示等,祝你编码愉快!