音乐播放器实现:前端HTML,CSS,JavaScript综合大项目

以下是一个完整、可直接运行的现代风格音乐播放器项目,使用纯 HTML + CSS + JavaScript(无任何框架),适合作为综合大项目练习或简历作品。

特点:

  • 响应式布局(手机 → 桌面都友好)
  • 深色主题 + 圆润现代 UI
  • 播放/暂停、上一首/下一首、进度条拖拽、音量控制
  • 歌曲列表 + 点击切换
  • 专辑封面旋转动画
  • 时间显示 + 自动下一首

完整代码(index.html)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>重阳音乐 · Music Player</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css"/>
  <style>
    :root {
      --bg: #0f0f0f;
      --card: #1a1a1a;
      --text: #e0e0e0;
      --accent: #e91e63;
      --accent-dark: #c2185b;
      --progress: #444;
    }

    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    body {
      font-family: system-ui, -apple-system, sans-serif;
      background: var(--bg);
      color: var(--text);
      min-height: 100vh;
      display: flex;
      justify-content: center;
      align-items: center;
      padding: 20px;
    }

    .player {
      background: var(--card);
      border-radius: 24px;
      width: 100%;
      max-width: 420px;
      overflow: hidden;
      box-shadow: 0 20px 40px rgba(0,0,0,0.6);
      backdrop-filter: blur(10px);
    }

    .cover {
      position: relative;
      height: 280px;
      overflow: hidden;
    }

    .cover img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      transition: transform 0.8s ease;
    }

    .cover.playing img {
      transform: scale(1.12) rotate(8deg);
    }

    .info {
      padding: 24px 28px 16px;
      text-align: center;
    }

    .title {
      font-size: 1.5rem;
      font-weight: 600;
      margin-bottom: 8px;
    }

    .artist {
      font-size: 0.95rem;
      color: #aaa;
      margin-bottom: 20px;
    }

    .progress-container {
      height: 6px;
      background: var(--progress);
      border-radius: 3px;
      margin: 0 28px 12px;
      cursor: pointer;
      position: relative;
    }

    .progress {
      height: 100%;
      width: 0%;
      background: var(--accent);
      border-radius: 3px;
      transition: width 0.2s linear;
    }

    .time {
      display: flex;
      justify-content: space-between;
      font-size: 0.8rem;
      color: #bbb;
      padding: 0 28px;
      margin-bottom: 20px;
    }

    .controls {
      display: flex;
      justify-content: center;
      align-items: center;
      gap: 28px;
      margin-bottom: 24px;
    }

    .btn {
      font-size: 1.6rem;
      color: var(--text);
      cursor: pointer;
      transition: all 0.3s;
    }

    .btn:hover { color: var(--accent); transform: scale(1.15); }

    .play-btn {
      font-size: 3.2rem;
      color: var(--accent);
    }

    .volume-container {
      display: flex;
      align-items: center;
      gap: 12px;
      padding: 0 28px 28px;
    }

    .volume-bar {
      flex: 1;
      height: 6px;
      background: var(--progress);
      border-radius: 3px;
      cursor: pointer;
      position: relative;
    }

    .volume-progress {
      height: 100%;
      width: 70%;
      background: var(--accent);
      border-radius: 3px;
    }

    .playlist {
      max-height: 240px;
      overflow-y: auto;
      padding: 0 16px 16px;
      border-top: 1px solid #333;
    }

    .song-item {
      display: flex;
      align-items: center;
      gap: 16px;
      padding: 12px 16px;
      border-radius: 12px;
      cursor: pointer;
      transition: all 0.2s;
    }

    .song-item:hover,
    .song-item.active {
      background: rgba(233,30,99,0.12);
    }

    .song-item img {
      width: 50px;
      height: 50px;
      border-radius: 8px;
      object-fit: cover;
    }

    .song-info {
      flex: 1;
    }

    .song-title {
      font-weight: 500;
      font-size: 0.95rem;
    }

    .song-artist {
      font-size: 0.8rem;
      color: #aaa;
    }

    @media (max-width: 480px) {
      .player { border-radius: 0; }
      .cover { height: 240px; }
    }
  </style>
</head>
<body>

<div class="player">
  <div class="cover">
    <img id="cover" src="https://images.unsplash.com/photo-1611339555312-e607c8352fd7?w=800" alt="cover">
  </div>

  <div class="info">
    <div class="title" id="title">暂无播放</div>
    <div class="artist" id="artist">—</div>

    <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">
      <i class="fas fa-backward btn" id="prev"></i>
      <i class="fas fa-play-circle play-btn" id="play"></i>
      <i class="fas fa-forward btn" id="next"></i>
    </div>
  </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">
    <!-- 歌曲列表由 JS 动态生成 -->
  </div>
</div>

<audio id="audio" preload="metadata"></audio>

<script>
// === 数据 ===
const songs = [
  {
    title: "Neon Dreams",
    artist: "Cyber Wave",
    src: "https://cdn.pixabay.com/audio/2023/08/07/audio_3b8d7d6e7a.mp3",
    cover: "https://images.unsplash.com/photo-1557672172-298e090bd0f1?w=800"
  },
  {
    title: "Midnight City",
    artist: "Electric Youth",
    src: "https://cdn.pixabay.com/audio/2022/03/15/audio_d4c8c7d9f2.mp3",
    cover: "https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=800"
  },
  {
    title: "Floating",
    artist: "Chillhop Music",
    src: "https://cdn.pixabay.com/audio/2024/01/12/audio_8f9e2c3b4d.mp3",
    cover: "https://images.unsplash.com/photo-1511671782779-c97d3d27c1d4?w=800"
  }
];

// === DOM 元素 ===
const audio = document.getElementById('audio');
const cover = document.getElementById('cover');
const titleEl = document.getElementById('title');
const artistEl = document.getElementById('artist');
const playBtn = document.getElementById('play');
const prevBtn = document.getElementById('prev');
const nextBtn = document.getElementById('next');
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');

let currentSong = 0;
let isPlaying = false;

// === 函数 ===

function loadSong(index) {
  const song = songs[index];
  titleEl.textContent = song.title;
  artistEl.textContent = song.artist;
  audio.src = song.src;
  cover.src = song.cover;
  highlightPlaylistItem(index);
}

function playSong() {
  playBtn.classList.replace('fa-play-circle', 'fa-pause-circle');
  document.querySelector('.cover').classList.add('playing');
  audio.play().catch(e => console.log("播放失败", e));
  isPlaying = true;
}

function pauseSong() {
  playBtn.classList.replace('fa-pause-circle', 'fa-play-circle');
  document.querySelector('.cover').classList.remove('playing');
  audio.pause();
  isPlaying = false;
}

function togglePlay() {
  isPlaying ? pauseSong() : playSong();
}

function prevSong() {
  currentSong = (currentSong - 1 + songs.length) % songs.length;
  loadSong(currentSong);
  playSong();
}

function nextSong() {
  currentSong = (currentSong + 1) % songs.length;
  loadSong(currentSong);
  playSong();
}

function updateProgress(e) {
  const {duration, currentTime} = e.srcElement;
  if (isNaN(duration)) return;

  const percent = (currentTime / duration) * 100;
  progress.style.width = percent + '%';

  const curMin = Math.floor(currentTime / 60);
  const curSec = Math.floor(currentTime % 60).toString().padStart(2, '0');
  currentTimeEl.textContent = `${curMin}:${curSec}`;

  const durMin = Math.floor(duration / 60);
  const durSec = Math.floor(duration % 60).toString().padStart(2, '0');
  durationEl.textContent = `${durMin}:${durSec}`;
}

function setProgress(e) {
  const width = this.clientWidth;
  const clickX = e.offsetX;
  const duration = audio.duration;
  audio.currentTime = (clickX / width) * duration;
}

function setVolume(e) {
  const width = this.clientWidth;
  const clickX = e.offsetX;
  const vol = clickX / width;
  audio.volume = vol;
  volumeProgress.style.width = (vol * 100) + '%';
}

function highlightPlaylistItem(index) {
  document.querySelectorAll('.song-item').forEach((item, i) => {
    item.classList.toggle('active', i === index);
  });
}

function renderPlaylist() {
  playlistEl.innerHTML = '';
  songs.forEach((song, index) => {
    const div = document.createElement('div');
    div.classList.add('song-item');
    div.innerHTML = `
      <img src="${song.cover}" alt="cover">
      <div class="song-info">
        <div class="song-title">${song.title}</div>
        <div class="song-artist">${song.artist}</div>
      </div>
    `;
    div.addEventListener('click', () => {
      currentSong = index;
      loadSong(index);
      playSong();
    });
    playlistEl.appendChild(div);
  });
}

// === 事件监听 ===
playBtn.addEventListener('click', togglePlay);
prevBtn.addEventListener('click', prevSong);
nextBtn.addEventListener('click', nextSong);
audio.addEventListener('timeupdate', updateProgress);
audio.addEventListener('ended', nextSong);
progressContainer.addEventListener('click', setProgress);
volumeBar.addEventListener('click', setVolume);

// 初始化
loadSong(currentSong);
renderPlaylist();
volumeProgress.style.width = (audio.volume * 100) + '%';

// 键盘控制(可选)
document.addEventListener('keydown', e => {
  if (e.code === 'Space') {
    e.preventDefault();
    togglePlay();
  }
});
</script>
</body>
</html>

如何使用 & 扩展建议

  1. 直接使用:把上面全部代码保存为 index.html,用浏览器打开即可。
  2. 替换歌曲:把 songs 数组里的 src 换成你自己的 mp3 链接(推荐免费版权音乐:Pixabay、Free Music Archive)。
  3. 本地音乐:可以改成 <input type="file" multiple accept="audio/*"> + URL.createObjectURL() 实现上传播放。
  4. 进阶方向
  • 拖拽排序歌单(Sortable.js)
  • 均衡器可视化(Web Audio API)
  • 播放模式(单曲循环、随机、列表循环)
  • 主题切换(明暗模式)
  • 后台播放(Service Worker + Media Session API)

这是一个非常适合前端综合练习的项目,涵盖了:

  • DOM 操作、事件委托
  • 音频 API(HTML5 <audio>
  • CSS 动画 & 响应式
  • 模块化思维(函数拆分)
  • 交互细节(拖拽进度、音量)

如果想加拖拽上传播放列表排序歌词同步Web Audio 可视化等高级功能,告诉我,我可以继续扩展代码!

文章已创建 4791

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部