📜  使用 JavaScript 创建音乐播放器

📅  最后修改于: 2021-08-31 07:14:35             🧑  作者: Mango

随着流媒体越来越多地被用户采用,在线媒体播放器已成为消费互联网媒体的必要条件。音乐播放器允许您在任何浏览器中欣赏音乐,并支持许多离线音乐播放器的功能。
我们将创建一个具有干净用户界面的音乐播放器,可用于在浏览器中播放音乐。我们还将实现搜索和音量控制等功能。 HTML 在 HTMLMediaElement 接口中有多种方法,可用于播放音频文件并控制其播放,而无需使用任何其他库。
我们将首先创建定义播放器结构的 HTML 布局,通过使用 CSS 设置样式使其看起来更美观,然后用 JavaScript 编写所有功能的播放器逻辑。
HTML 布局
HTML 布局定义了将在页面上显示的元素结构。播放器可以分为以下几个部分:

  • 详细信息部分:此部分显示正在播放的当前曲目的详细信息。它包括曲目编号、曲目专辑、曲目名称和曲目艺术家。
  • 按钮部分:此部分显示用于控制曲目播放的按钮。它包括播放/暂停按钮、上一首和下一首曲目按钮。他们会有一个 onclick() 方法来调用 JavaScript 文件中定义的特定函数。
  • 滑块部分:此部分包含可用于控制播放和音量的搜索滑块和音量滑块。

我们将使用 FontAwesome 图标来获取页面上使用的所有按钮的图标。我们稍后将编写的自定义 CSS 和 JavaScript 也在文件中链接。
HTML代码如下:

html



  Simple Music Player
  
  
 
  
  


  
           
      
PLAYING x OF y
      
      
Track Name
      
Track Artist
    
           
      
               
      
               
      
               
    
           
      
00:00
             
00:00
    
           
                         
  
       


css
body {
  background-color: lightgreen;
 
  /* Smoothly transition the background color */
  transition: background-color .5s;
}
 
/* Using flex with the column direction to
   align items in a vertical direction */
.player {
  height: 95vh;
  display: flex;
  align-items: center;
  flex-direction: column;
  justify-content: center;
}
 
.details {
  display: flex;
  align-items: center;
  flex-direction: column;
  justify-content: center;
  margin-top: 25px;
}
 
.track-art {
  margin: 25px;
  height: 250px;
  width: 250px;
  background-image: URL(
     "https://source.unsplash.com/Qrspubmx6kE/640x360");
  background-size: cover;
  background-position: center;
  border-radius: 15%;
}
 
/* Changing the font sizes to suitable ones */
.now-playing {
  font-size: 1rem;
}
 
.track-name {
  font-size: 3rem;
}
 
.track-artist {
  font-size: 1.5rem;
}
 
/* Using flex with the row direction to
   align items in a horizontal direction */
.buttons {
  display: flex;
  flex-direction: row;
  align-items: center;
}
 
.playpause-track,
.prev-track,
.next-track {
  padding: 25px;
  opacity: 0.8;
 
  /* Smoothly transition the opacity */
  transition: opacity .2s;
}
 
/* Change the opacity when mouse is hovered */
.playpause-track:hover,
.prev-track:hover,
.next-track:hover {
  opacity: 1.0;
}
 
/* Define the slider width so that it scales properly */
.slider_container {
  width: 75%;
  max-width: 400px;
  display: flex;
  justify-content: center;
  align-items: center;
}
 
/* Modify the appearance of the slider */
.seek_slider, .volume_slider {
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  height: 5px;
  background: black;
  opacity: 0.7;
  -webkit-transition: .2s;
  transition: opacity .2s;
}
 
/* Modify the appearance of the slider thumb */
.seek_slider::-webkit-slider-thumb,
.volume_slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  width: 15px;
  height: 15px;
  background: white;
  cursor: pointer;
  border-radius: 50%;
}
 
/* Change the opacity when mouse is hovered */
.seek_slider:hover,
.volume_slider:hover {
  opacity: 1.0;
}
 
.seek_slider {
  width: 60%;
}
 
.volume_slider {
  width: 30%;
}
 
.current-time,
.total-duration {
  padding: 10px;
}
 
i.fa-volume-down,
i.fa-volume-up {
  padding: 10px;
}
 
/* Change the mouse cursor to a pointer
   when hovered over */
i.fa-play-circle,
i.fa-pause-circle,
i.fa-step-forward,
i.fa-step-backward {
  cursor: pointer;
}


javascript
// Select all the elements in the HTML page
// and assign them to a variable
let now_playing = document.querySelector(".now-playing");
let track_art = document.querySelector(".track-art");
let track_name = document.querySelector(".track-name");
let track_artist = document.querySelector(".track-artist");
 
let playpause_btn = document.querySelector(".playpause-track");
let next_btn = document.querySelector(".next-track");
let prev_btn = document.querySelector(".prev-track");
 
let seek_slider = document.querySelector(".seek_slider");
let volume_slider = document.querySelector(".volume_slider");
let curr_time = document.querySelector(".current-time");
let total_duration = document.querySelector(".total-duration");
 
// Specify globally used values
let track_index = 0;
let isPlaying = false;
let updateTimer;
 
// Create the audio element for the player
let curr_track = document.createElement('audio');
 
// Define the list of tracks that have to be played
let track_list = [
  {
    name: "Night Owl",
    artist: "Broke For Free",
    image: "Image URL",
    path: "Night_Owl.mp3"
  },
  {
    name: "Enthusiast",
    artist: "Tours",
    image: "Image URL",
    path: "Enthusiast.mp3"
  },
  {
    name: "Shipping Lanes",
    artist: "Chad Crouch",
    image: "Image URL",
    path: "Shipping_Lanes.mp3",
  },
];


javascript
function loadTrack(track_index) {
  // Clear the previous seek timer
  clearInterval(updateTimer);
  resetValues();
 
  // Load a new track
  curr_track.src = track_list[track_index].path;
  curr_track.load();
 
  // Update details of the track
  track_art.style.backgroundImage =
     "url(" + track_list[track_index].image + ")";
  track_name.textContent = track_list[track_index].name;
  track_artist.textContent = track_list[track_index].artist;
  now_playing.textContent =
     "PLAYING " + (track_index + 1) + " OF " + track_list.length;
 
  // Set an interval of 1000 milliseconds
  // for updating the seek slider
  updateTimer = setInterval(seekUpdate, 1000);
 
  // Move to the next track if the current finishes playing
  // using the 'ended' event
  curr_track.addEventListener("ended", nextTrack);
 
  // Apply a random background color
  random_bg_color();
}
 
function random_bg_color() {
  // Get a random number between 64 to 256
  // (for getting lighter colors)
  let red = Math.floor(Math.random() * 256) + 64;
  let green = Math.floor(Math.random() * 256) + 64;
  let blue = Math.floor(Math.random() * 256) + 64;
 
  // Construct a color withe the given values
  let bgColor = "rgb(" + red + ", " + green + ", " + blue + ")";
 
  // Set the background to the new color
  document.body.style.background = bgColor;
}
 
// Function to reset all values to their default
function resetValues() {
  curr_time.textContent = "00:00";
  total_duration.textContent = "00:00";
  seek_slider.value = 0;
}


javascript
function playpauseTrack() {
  // Switch between playing and pausing
  // depending on the current state
  if (!isPlaying) playTrack();
  else pauseTrack();
}
 
function playTrack() {
  // Play the loaded track
  curr_track.play();
  isPlaying = true;
 
  // Replace icon with the pause icon
  playpause_btn.innerHTML = '';
}
 
function pauseTrack() {
  // Pause the loaded track
  curr_track.pause();
  isPlaying = false;
 
  // Replace icon with the play icon
  playpause_btn.innerHTML = '';;
}
 
function nextTrack() {
  // Go back to the first track if the
  // current one is the last in the track list
  if (track_index < track_list.length - 1)
    track_index += 1;
  else track_index = 0;
 
  // Load and play the new track
  loadTrack(track_index);
  playTrack();
}
 
function prevTrack() {
  // Go back to the last track if the
  // current one is the first in the track list
  if (track_index > 0)
    track_index -= 1;
  else track_index = track_list.length;
   
  // Load and play the new track
  loadTrack(track_index);
  playTrack();
}


javascript
function seekTo() {
  // Calculate the seek position by the
  // percentage of the seek slider
  // and get the relative duration to the track
  seekto = curr_track.duration * (seek_slider.value / 100);
 
  // Set the current track position to the calculated seek position
  curr_track.currentTime = seekto;
}
 
function setVolume() {
  // Set the volume according to the
  // percentage of the volume slider set
  curr_track.volume = volume_slider.value / 100;
}
 
function seekUpdate() {
  let seekPosition = 0;
 
  // Check if the current track duration is a legible number
  if (!isNaN(curr_track.duration)) {
    seekPosition = curr_track.currentTime * (100 / curr_track.duration);
    seek_slider.value = seekPosition;
 
    // Calculate the time left and the total duration
    let currentMinutes = Math.floor(curr_track.currentTime / 60);
    let currentSeconds = Math.floor(curr_track.currentTime - currentMinutes * 60);
    let durationMinutes = Math.floor(curr_track.duration / 60);
    let durationSeconds = Math.floor(curr_track.duration - durationMinutes * 60);
 
    // Add a zero to the single digit time values
    if (currentSeconds < 10) { currentSeconds = "0" + currentSeconds; }
    if (durationSeconds < 10) { durationSeconds = "0" + durationSeconds; }
    if (currentMinutes < 10) { currentMinutes = "0" + currentMinutes; }
    if (durationMinutes < 10) { durationMinutes = "0" + durationMinutes; }
 
    // Display the updated duration
    curr_time.textContent = currentMinutes + ":" + currentSeconds;
    total_duration.textContent = durationMinutes + ":" + durationSeconds;
  }
}


javascript
// Load the first track in the tracklist
loadTrack(track_index);


CSS 样式
使用 CSS,我们可以设置不同部分的样式,使其更具视觉吸引力:

  • flex 布局用于排列播放器的各种元素,并将它们与页面中间对齐。
  • 轨道艺术图像被赋予一个固定的尺寸,并使用 border-radius 属性进行四舍五入。
  • 这两个滑块已通过使用外观属性从其默认外观进行了修改。更改高度和背景以适合配色方案。它们还具有轻微的透明度,可以使用 transition 属性平滑过渡到完全不透明度。
  • 所有播放控件都设置了其光标属性,以便鼠标悬停在其上时它会更改为指针。

css

body {
  background-color: lightgreen;
 
  /* Smoothly transition the background color */
  transition: background-color .5s;
}
 
/* Using flex with the column direction to
   align items in a vertical direction */
.player {
  height: 95vh;
  display: flex;
  align-items: center;
  flex-direction: column;
  justify-content: center;
}
 
.details {
  display: flex;
  align-items: center;
  flex-direction: column;
  justify-content: center;
  margin-top: 25px;
}
 
.track-art {
  margin: 25px;
  height: 250px;
  width: 250px;
  background-image: URL(
     "https://source.unsplash.com/Qrspubmx6kE/640x360");
  background-size: cover;
  background-position: center;
  border-radius: 15%;
}
 
/* Changing the font sizes to suitable ones */
.now-playing {
  font-size: 1rem;
}
 
.track-name {
  font-size: 3rem;
}
 
.track-artist {
  font-size: 1.5rem;
}
 
/* Using flex with the row direction to
   align items in a horizontal direction */
.buttons {
  display: flex;
  flex-direction: row;
  align-items: center;
}
 
.playpause-track,
.prev-track,
.next-track {
  padding: 25px;
  opacity: 0.8;
 
  /* Smoothly transition the opacity */
  transition: opacity .2s;
}
 
/* Change the opacity when mouse is hovered */
.playpause-track:hover,
.prev-track:hover,
.next-track:hover {
  opacity: 1.0;
}
 
/* Define the slider width so that it scales properly */
.slider_container {
  width: 75%;
  max-width: 400px;
  display: flex;
  justify-content: center;
  align-items: center;
}
 
/* Modify the appearance of the slider */
.seek_slider, .volume_slider {
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  height: 5px;
  background: black;
  opacity: 0.7;
  -webkit-transition: .2s;
  transition: opacity .2s;
}
 
/* Modify the appearance of the slider thumb */
.seek_slider::-webkit-slider-thumb,
.volume_slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  width: 15px;
  height: 15px;
  background: white;
  cursor: pointer;
  border-radius: 50%;
}
 
/* Change the opacity when mouse is hovered */
.seek_slider:hover,
.volume_slider:hover {
  opacity: 1.0;
}
 
.seek_slider {
  width: 60%;
}
 
.volume_slider {
  width: 30%;
}
 
.current-time,
.total-duration {
  padding: 10px;
}
 
i.fa-volume-down,
i.fa-volume-up {
  padding: 10px;
}
 
/* Change the mouse cursor to a pointer
   when hovered over */
i.fa-play-circle,
i.fa-pause-circle,
i.fa-step-forward,
i.fa-step-backward {
  cursor: pointer;
}

HTML 布局和 CSS 样式的结果将呈现以下外观:

html_css_output

播放器的 JavaScript 逻辑:
播放器的逻辑在 JavaScript 文件中定义。有几个函数协同工作来处理播放器的所有功能。
第 1 步:定义所有变量并访问 HTML 元素
HTML 布局中需要动态更改的元素首先使用 querySelector() 方法选择。然后为它们分配变量名称,以便可以访问和修改它们。还定义了将在整个程序中访问的其他变量。

javascript

// Select all the elements in the HTML page
// and assign them to a variable
let now_playing = document.querySelector(".now-playing");
let track_art = document.querySelector(".track-art");
let track_name = document.querySelector(".track-name");
let track_artist = document.querySelector(".track-artist");
 
let playpause_btn = document.querySelector(".playpause-track");
let next_btn = document.querySelector(".next-track");
let prev_btn = document.querySelector(".prev-track");
 
let seek_slider = document.querySelector(".seek_slider");
let volume_slider = document.querySelector(".volume_slider");
let curr_time = document.querySelector(".current-time");
let total_duration = document.querySelector(".total-duration");
 
// Specify globally used values
let track_index = 0;
let isPlaying = false;
let updateTimer;
 
// Create the audio element for the player
let curr_track = document.createElement('audio');
 
// Define the list of tracks that have to be played
let track_list = [
  {
    name: "Night Owl",
    artist: "Broke For Free",
    image: "Image URL",
    path: "Night_Owl.mp3"
  },
  {
    name: "Enthusiast",
    artist: "Tours",
    image: "Image URL",
    path: "Enthusiast.mp3"
  },
  {
    name: "Shipping Lanes",
    artist: "Chad Crouch",
    image: "Image URL",
    path: "Shipping_Lanes.mp3",
  },
];

第 2 步:从曲目列表中加载新曲目
所有必须播放的曲目都在曲目列表中定义为对象。这些对象包含名称、艺术家、图像和轨道路径等属性。然后可以使用其轨道索引访问每个轨道。
为了加载轨道,定义了一个函数loadTrack() 来处理以下事情:

  • 重置上一曲目的所有值
    创建了一个 resetValues()函数,该函数处理在新轨道开始之前将持续时间值和滑块重置为它们的初始值。这可以防止在加载新轨道时跳跃搜索滑块。
  • 加载轨道
    使用其 src 属性为音频元素分配一个新源。它可以从文件系统或 URL 中获得任何路径。然后在音频元素上使用 load() 方法来准备轨道。
  • 更新要显示的轨道艺术
    轨道艺术是从数组中获取并在 backgroundImage 属性的帮助下分配的。
  • 更新要显示的曲目详细信息
    轨道详细信息从数组中获取并在 textContent 属性的帮助下进行分配。
  • 将事件侦听器添加到轨道
    media 元素添加了两个事件侦听器,第一个用于更新当前搜索位置,第二个用于在当前曲目完成时加载下一个曲目。
  • 设置随机彩色背景
    通过随机化使用的红色、绿色和蓝色值并将其设置为颜色来生成彩色背景。该效果通过使用背景颜色的过渡属性进行动画处理。

javascript

function loadTrack(track_index) {
  // Clear the previous seek timer
  clearInterval(updateTimer);
  resetValues();
 
  // Load a new track
  curr_track.src = track_list[track_index].path;
  curr_track.load();
 
  // Update details of the track
  track_art.style.backgroundImage =
     "url(" + track_list[track_index].image + ")";
  track_name.textContent = track_list[track_index].name;
  track_artist.textContent = track_list[track_index].artist;
  now_playing.textContent =
     "PLAYING " + (track_index + 1) + " OF " + track_list.length;
 
  // Set an interval of 1000 milliseconds
  // for updating the seek slider
  updateTimer = setInterval(seekUpdate, 1000);
 
  // Move to the next track if the current finishes playing
  // using the 'ended' event
  curr_track.addEventListener("ended", nextTrack);
 
  // Apply a random background color
  random_bg_color();
}
 
function random_bg_color() {
  // Get a random number between 64 to 256
  // (for getting lighter colors)
  let red = Math.floor(Math.random() * 256) + 64;
  let green = Math.floor(Math.random() * 256) + 64;
  let blue = Math.floor(Math.random() * 256) + 64;
 
  // Construct a color withe the given values
  let bgColor = "rgb(" + red + ", " + green + ", " + blue + ")";
 
  // Set the background to the new color
  document.body.style.background = bgColor;
}
 
// Function to reset all values to their default
function resetValues() {
  curr_time.textContent = "00:00";
  total_duration.textContent = "00:00";
  seek_slider.value = 0;
}

第 3 步:配置播放器按钮
函数playTrack() 处理当前加载曲目的播放。 HTMLMediaElement API 的 play() 方法用于此函数。按钮的图标也会更改为暂停图标。这是通过使用 FontAwesome 库中的图标之一并使用 innerHTML 插入它来完成的。
函数pauseTrack() 处理当前加载曲目的播放。 HTMLMediaElement API 的 pause() 方法用于此函数。按钮的图标也变回播放图标。这是通过使用 FontAwesome 库中的图标之一并使用 innerHTML 插入它来完成的。
这两个函数的调用取决于曲目当前是否正在播放。 playpause()函数处理曲目的实际播放/暂停控制。
一个函数prevTrack() 处理加载上一首曲目并向后移动索引。当索引到达第一个轨道时,索引被重置为最后一个轨道。上面定义的 loadTrack() 方法用于加载新轨道。
类似地,函数nextTrack() 处理下一个曲目的加载并向前移动索引。当索引到达最后一首曲目时,将索引重置为第一首曲目。上面定义的 loadTrack() 方法用于加载新轨道。

javascript

function playpauseTrack() {
  // Switch between playing and pausing
  // depending on the current state
  if (!isPlaying) playTrack();
  else pauseTrack();
}
 
function playTrack() {
  // Play the loaded track
  curr_track.play();
  isPlaying = true;
 
  // Replace icon with the pause icon
  playpause_btn.innerHTML = '';
}
 
function pauseTrack() {
  // Pause the loaded track
  curr_track.pause();
  isPlaying = false;
 
  // Replace icon with the play icon
  playpause_btn.innerHTML = '';;
}
 
function nextTrack() {
  // Go back to the first track if the
  // current one is the last in the track list
  if (track_index < track_list.length - 1)
    track_index += 1;
  else track_index = 0;
 
  // Load and play the new track
  loadTrack(track_index);
  playTrack();
}
 
function prevTrack() {
  // Go back to the last track if the
  // current one is the first in the track list
  if (track_index > 0)
    track_index -= 1;
  else track_index = track_list.length;
   
  // Load and play the new track
  loadTrack(track_index);
  playTrack();
}

第 4 步:配置滑块部分
我们将设置两个滑块来控制搜索滑块和音量滑块。

  • 搜索滑块
    搜索滑块通过使用轨道的当前时间更新滑块来显示滑块上的当前播放位置。创建了一个新函数seekUpdate() 处理相对于轨道当前时间的搜索滑块的更新。使用 value 属性计算和设置搜索滑块位置。
    现在,每次轨道进一步前进时都必须调用此函数。这可以通过安排它每秒更新来完成。这可以使用间隔为 1000 毫秒的 setInterval() 方法来完成。每次加载新曲目时都会清除此计时器。
    此函数还处理已用时间和曲目总持续时间的更改,每次此函数触发时都会更新。分和秒分别计算并正确格式化以显示。
  • 音量滑块
    音量滑块用于显示曲目的当前音量。创建了一个新函数setVolume() ,它在用户更改音量滑块时处理音量滑块的设置。

javascript

function seekTo() {
  // Calculate the seek position by the
  // percentage of the seek slider
  // and get the relative duration to the track
  seekto = curr_track.duration * (seek_slider.value / 100);
 
  // Set the current track position to the calculated seek position
  curr_track.currentTime = seekto;
}
 
function setVolume() {
  // Set the volume according to the
  // percentage of the volume slider set
  curr_track.volume = volume_slider.value / 100;
}
 
function seekUpdate() {
  let seekPosition = 0;
 
  // Check if the current track duration is a legible number
  if (!isNaN(curr_track.duration)) {
    seekPosition = curr_track.currentTime * (100 / curr_track.duration);
    seek_slider.value = seekPosition;
 
    // Calculate the time left and the total duration
    let currentMinutes = Math.floor(curr_track.currentTime / 60);
    let currentSeconds = Math.floor(curr_track.currentTime - currentMinutes * 60);
    let durationMinutes = Math.floor(curr_track.duration / 60);
    let durationSeconds = Math.floor(curr_track.duration - durationMinutes * 60);
 
    // Add a zero to the single digit time values
    if (currentSeconds < 10) { currentSeconds = "0" + currentSeconds; }
    if (durationSeconds < 10) { durationSeconds = "0" + durationSeconds; }
    if (currentMinutes < 10) { currentMinutes = "0" + currentMinutes; }
    if (durationMinutes < 10) { durationMinutes = "0" + durationMinutes; }
 
    // Display the updated duration
    curr_time.textContent = currentMinutes + ":" + currentSeconds;
    total_duration.textContent = durationMinutes + ":" + durationSeconds;
  }
}

第 5 步:启动播放器
通过调用 loadTrack()函数加载第一条轨道。这将从曲目列表中加载第一首曲目并更新曲目的所有详细信息。然后用户可以使用播放按钮开始播放曲目。上一首和下一首曲目按钮将分别加载上一首和下一首曲目并开始播放。
曲目播放完毕后,将自动加载下一曲目。用户可以使用搜索滑块搜索轨道中的某个位置。音量也可以使用音量滑块进行调整。

javascript

// Load the first track in the tracklist
loadTrack(track_index);

最后演示
播放器现在可以在任何浏览器中使用了。可以将新曲目添加到曲目列表以播放您选择的音乐。

在线试用: https : //ide.geeksforgeeks.org/tryit。 PHP/T3gdWUn4aX
源代码: https : //github.com/sayantanm19/js-music-player