如何用Vanilla JavaScript构建一个音乐播放器

180 阅读7分钟

用Vanilla JavaScript构建一个音乐播放器

如今,流媒体是人们在互联网上的一项主要活动。出于这个原因,创建一个具有常规音乐播放器应用功能的媒体平台是一个伟大而有趣的项目。

在这篇文章中,我们将向你展示如何使用HTML、CSS、JavaScript和HTML5音频API创建一个音乐播放器,它可以在浏览器上播放音乐。

前提条件

  • 一个好的代码编辑器。比如说。Visual Studio Code。
  • 一些关于HTML、CSS和JavaScript的知识。

设计音乐播放器

你要做的第一件事是在你的代码编辑器中创建三个文件。

为你的HTML代码命名为index.html ,为你的CSS代码命名为style.css ,为JavaScript命名为script.js

你还需要下载三首歌曲作为.mp3 文件,以及它们相应的图片作为.jpg 文件。你可以在我的Github资源库中找到这个项目所使用的图片和音乐。

在你的index.html 文件中,复制并粘贴以下代码来创建音乐播放器的结构。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.10.2/css/all.min.css"
    />
    <link rel="stylesheet" href="style.css" />
    <title>Music Player</title>
  </head>
  <body>
    <h1>Music Player</h1>
    <div class="music-container" id="music-container">
      <div class="music-info">
        <h4 id="title"></h4>
        <div class="progress-container" id="progress-container">
          <div class="progress" id="progress"></div>
        </div>
      </div>
      <audio src="music/Polo G – I Know.mp3" id="audio"></audio>
      <div class="img-container">
        <img src="images/Polo G – I Know.jpg" alt="music-cover" id="cover" />
      </div>
      <div class="navigation">
        <button id="prev" class="action-btn">
          <i class="fas fa-backward"></i>
        </button>
        <button id="play" class="action-btn action-btn-big">
          <i class="fas fa-play"></i>
        </button>
        <button id="next" class="action-btn">
          <i class="fas fa-forward"></i>
        </button>
      </div>
    </div>
    <script src="script.js"></script>
  </body>
</html>

音乐容器包含音乐标题、进度条、上一个、播放、下一个按钮图标和音乐图片。这里是index.html文件的CSS。

@import url('https://fonts.googleapis.com/css?family=Lato&display=swap');

* {
  box-sizing: border-box;
}

body {
  background-image: linear-gradient(
    0deg,
    #8A2BE2,
    #9370DB
  );
  height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-family: 'Lato', sans-serif;
  margin: 0;
}

/* styling the outer music container */
.music-container {
  background-color: #efefef;
  box-shadow: inset -1px -1px 5px rgba(33,33,33, 0.5), inset 1px 1px 5px rgba(33,33,33,0.5); 
  display: flex;
  padding: 20px 30px;
  position: relative;
  margin: 100px 0;
  z-index: 10;
}

.img-container {
  position: relative;
  width: 110px;
}

/* styling the image to look like a disc by placing a small circle in it*/
.img-container::after {
  content: '';
  background-color: #fff;
  border-radius: 50%;
  position: absolute;
  bottom: 100%;
  left: 50%;
  width: 20px;
  height: 20px;
  transform: translate(-50%, 50%);
}

/* using animation to make the image rotate continiously when music is playing */
.img-container img {
  border-radius: 50%;
  object-fit: cover;
  height: 110px;
  width: inherit;
  position: absolute;
  bottom: 0;
  left: 0;
  animation: rotate 3s linear infinite;

  animation-play-state: paused;
}

/* making sure the image rotates only when the music is playing */
.music-container.play .img-container img {
  animation-play-state: running;
}

/* creating the animation keyframe and setting the image to rotate 360 degrees continiously */
@keyframes rotate {
  from {
    transform: rotate(0deg);
  }

  to {
    transform: rotate(360deg);
  }
}


.navigation {
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
}

/* styling the navigation buttons */
.action-btn {
  border: 0;
  border-radius: 20px;
  font-size: 20px;
  cursor: pointer;
  padding: 10px;
  margin: 0 20px;
}

.action-btn.action-btn-big {
  font-size: 30px;
  outline: none;
}

.action-btn:focus {
  outline: 0;
}

/* styling the music-info div by placing it under the music container */
.music-info {
  background-color: rgba(255, 255, 255, 0.5);
  border-radius: 15px 15px 0 0;
  position: absolute;
  top: 0;
  left: 20px;
  width: calc(100% - 40px);
  padding: 10px 10px 10px 150px;
  opacity: 0;
  transform: translateY(0%);
  transition: transform 0.3s ease-in, opacity 0.3s ease-in;
  z-index: 0;
}

/* making the music info pop up when the music playing using the transform property*/
.music-container.play .music-info {
  opacity: 1;
  transform: translateY(-100%);
}

.music-info h4 {
  margin: 0;
}

/* styling the song progress container */
.progress-container {
  background: #fff;
  border-radius: 5px;
  cursor: pointer;
  margin: 10px 0;
  height: 4px;
  width: 100%;
}

/* styling the actual progress bar and making it flow along with the song*/
.progress {
  background-color: #fe8daa;
  border-radius: 5px;
  height: 100%;
  width: 0%;
  transition: width 0.1s linear;
}

你已经成功地设计了音乐播放器,并在音乐主题图像中添加了一点动画,使其在音乐播放时旋转。

在对JavaScript进行编码后,你将看到它的实际效果。

下面是音乐播放器在这一点上的样子。

music-player

JavaScript

在你的JavaScript文件中,我们需要做的第一件事是使用下面的代码片段将我们需要的不同元素带入DOM中。

const musicContainer = document.getElementById('music-container');
const playBtn = document.getElementById('play');
const prevBtn = document.getElementById('prev');
const nextBtn = document.getElementById('next');

const audio = document.getElementById('audio');
const progress = document.getElementById('progress');
const progressContainer = document.getElementById('progress-container');
const title = document.getElementById('title');
const cover = document.getElementById('cover');
const currTime = document.querySelector('#currTime');
const durTime = document.querySelector('#durTime');

下一步是将你的歌曲与它们的歌名排列在一个数组中。你的歌曲标题需要与你的音乐文件夹中的作品相匹配。

下面是一个包含我为这个项目挑选的歌曲的数组。

const songs = [
    'Juice WRLD Ft Benny Blanco - Real Shit',
    'Lil Baby, Lil Durk ft Rodwave - Rich Off Pain',
    'Polo G – I Know'
];

我们需要通过将默认的初始歌曲索引设置为2,即数组中的第三首歌曲来跟踪这些歌曲。

let songIndex = 2;

loadSong函数

接下来,你需要使用下面的代码将你的歌曲最初加载到文档对象模块(DOM)。

loadSong(songs[songIndex]);

function loadSong(song) {
  title.innerText = song;
  audio.src = `music/${song}.mp3`;
  cover.src = `images/${song}.jpg`;
}

在这个函数中,你正在更新歌曲标题、音频源和图像源。

像前面提到的,如果你想使用这个确切的脚本,你的歌曲必须是.mp3 文件,你的图片必须是.jpg 文件。

你的歌曲名称也必须与你的图片名称一致。在这一点上,如果你改变了你的歌曲索引,歌曲图像将随着你输入的相应索引而改变。

下一个功能是让play button ,播放你的歌曲。在播放歌曲时,它也应该改变为pause button

要做到这一点,我们需要在代码的底部创建一个事件监听器来监听播放和暂停按钮的点击事件。

playBtn.addEventListener('click', () => {
  const isPlaying = musicContainer.classList.contains('play');

  if (isPlaying) {
    pauseSong();
  } else {
    playSong();
  }
});

现在,你需要创建playSong()pauseSong() 函数。

playSong函数

使用下面的代码片断来实现playSong函数。

function playSong() {
  musicContainer.classList.add('play');
  playBtn.querySelector('i.fas').classList.remove('fa-play');
  playBtn.querySelector('i.fas').classList.add('fa-pause');

  audio.play();
}

pauseSong函数

下面是实现pauseSong() 函数的代码。它的作用与playSong() 正好相反。

function pauseSong() {
  musicContainer.classList.remove('play');
  playBtn.querySelector('i.fas').classList.add('fa-play');
  playBtn.querySelector('i.fas').classList.remove('fa-pause');

  audio.pause();
}

此时,当你点击play button ,它会自动切换到pause button 。歌曲的详细信息也会弹出,并且歌曲图像会开始旋转。

当点击pause button ,则会出现完全相反的情况。

请注意,audio.play()audio.pause() 函数实际上是分别播放和暂停歌曲的。

接下来,你需要实现previousnext 按钮,你将需要这些操作的事件监听器。

prevBtn.addEventListener('click', prevSong);
nextBtn.addEventListener('click', nextSong);

现在,你必须创建prevSongnextSong 函数。

prevSong函数

当你点击previous button ,播放器应该播放歌曲数组中的前一首歌曲。你可以通过递减歌曲索引来做到这一点。

你应该确保歌曲索引的值不会降低到0以下。如果它降低了,播放器应该循环回到最初的songs.length -1

你可以用下面的代码实现这些功能。

function prevSong() {
    songIndex--;

    if (songIndex < 0) {
      songIndex = songs.length - 1;
    }

    loadSong(songs[songIndex]);

    playSong();
}

nextSong函数

这一次,当你点击下一个按钮时,你想让歌曲跳到下一首歌曲。你可以通过增加歌曲索引的值来做到这一点。你还必须检查歌曲索引的值,使其不超过songs.length - 1 的值。

function nextSong() {
  songIndex++;

  if (songIndex > songs.length - 1) {
    songIndex = 0;
  }

  loadSong(songs[songIndex]);

  playSong();
}

你需要做的最后一件事是实现进度条。进度条包含歌曲标题和正在播放的歌曲的进度,以及计时器。

为了实现这一点,你需要给audio 标签添加一个事件监听器,通过API的HTML音频标签,有一个叫做timeupdate 的事件,你将使用下面的代码在你的事件监听器中调用它。

audio.addEventListener('timeupdate', updateProgress);

updateProgress函数

这个函数将接收一个事件对象,在这个事件对象中,你可以得到歌曲的持续时间和当前时间。你还将通过用当前时间除以歌曲持续时间来设置进度的百分比。

下面是在你的音乐播放器中持续更新进度条的代码。

function updateProgress(e) {
  const { duration, currentTime } = e.srcElement;
  const progressPercent = (currentTime / duration) * 100;
  progress.style.width = `${progressPercent}%`;
}

其他功能

当你的音乐正在播放时,你可能想点击进度条上的任何地方,让歌曲跳到该点。

为了做到这一点,我们必须创建一个事件监听器,它将使用下面的片段来监听对进度条的点击。

progressContainer.addEventListener('click', setProgress);

现在,我们必须创建setProgress 函数。

setProgress函数

在这个函数中,我们将传入一个事件对象。我们将以宽度为目标,并将其设置为this.clientWidth 。然后,我们将使用e.offsetX ,获得我们在进度条的X轴上点击的确切位置。

接下来我们要得到的是歌曲的完整持续时间,通过audio.duration

最后,你必须将当前时间设置到你点击的地方。

下面是设置我们的进度条的代码。

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

  audio.currentTime = (clickX / width) * duration;
}

我们要实现的最后一个功能是使音乐播放器在当前歌曲结束时自动播放下一首歌曲。

要做到这一点,我们必须为音频API创建一个事件监听器,并监听ended, ,然后调用我们先前创建的nextSong 函数即可。

audio.addEventListener('ended', nextSong);

下面是一个音乐播放器及其所有功能的使用视频。

结语

在本教程中,我们使用了CSS动画、纯香草的JavaScript和现代ES6实践。就这样,我们在浏览器中拥有了一个功能齐全的音乐播放器应用。

你也可以通过克隆我Github账户上的存储库来为该项目添加额外的功能。

我们应用程序的源代码可以在GitHub上找到。