ساخت موزیک پلیر (پخش کننده صدا) با Javascript

How to Create A Music Player with JavaScript

16 اسفند 1400
How-to-Create-A-Music-Player-with-JavaScript

برای ساخت این پروژه از تگ audio استفاده می کنیم. اطلاعات بیش تر در مورد این تگ در این نشانی آمده است. تصویر کامل برنامه در زیر آمده است:

ساختار پروژه

برای ساخت این پروژه به دو پوشه به نام های music و images و فایل های index.html، style.css و script.js نیاز داریم.

پوشه images برای نگهداری تصاویر است.این تصاویر برای عکس کاور آهنگ هستند. پوشه music دربردارنده لیست موزیک ها است. هر تعداد موزیک که بخواهیم می توانیم در این پوشه بگذاریم. برنامه آهنگ های موجود در این پوشه را پخش می کند.

فایل index.html برای تعیین ساختار برنامه است. در این فایل عناصر مختلف خود مانند تگ body و... را تعریف می کنیم. فایل های style.css و script.js در این فایل قرار می گیرند. style.css در  تگ head و script.js در انتهای تگ body قرار می گیرد.

style.css برای استایل دهی و script.js برای پیاده سازی منطق برنامه به کار می رود. تک تک فایل ها را در زیر توضیح می دهیم و به نحو پیاده سازی آن ها می پردازیم.

فایل 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/ukulele.mp3" id="audio"></audio>

      <div class="img-container">
        <img src="images/ukulele.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>

برای استفاده از آیکون ها و زیباتر سازی برنامه از fontawesome استفاده می کنیم:

<link
  rel="stylesheet"
  href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.10.2/css/all.min.css"
/>

در ادامه به یک نگهدارنده یا کانتینر اصلی نیاز داریم. این نگهدارنده یک تگ div با کلاس music-container است.

اجزای اصلی برنامه در این قسمت قرار می گیرند و همان طور که قبلا هم گفتیم این تگ یک نگهدارنده است. به چندین تگ دیگر نیز نیاز داریم. این تگ ها در زیر آمده اند:

  • یک تگ div برای نشان دادن اطلاعات آهنگ با کلاس music-info
<div class="music-info">
   <h4 id="title"></h4>
   <div class="progress-container" id="progress-container">
     <div class="progress" id="progress"></div>
   </div>
 </div>
  • یک تگ audio برای پخش آهنگ
     <audio src="music/ukulele.mp3" id="audio"></audio>
  • یک تگ div برای نشان دادن تصویر با کلاس img-container
<div class="img-container">
    <img src="images/ukulele.jpg" alt="music-cover" id="cover" />
</div>

  • یک تگ div برای نشان دادن دکمه های مختلف برای کار کردن با آهنگ ها با کلاس navigation
<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>

برای درک بهتر به تصویر زیر نگاه کنید:

اگر فایل index.html را در مرورگز باز کنیم باید تصویر زیر را ببینیم:

این فایل بدون استایل است.گام بعدی افزودن استایل یا همان استفاده از css است.

فایل style.css

این برای زیبا سازی و استایل دهی به برنامه است. کد آن در زیر آمده است:

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

* {
  box-sizing: border-box;
}

body {
  background-image: linear-gradient(
    0deg,
    rgba(247, 247, 247, 1) 23.8%,
    rgba(252, 221, 221, 1) 92%
  );
  height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-family: 'Lato', sans-serif;
  margin: 0;
}

.music-container {
  background-color: #fff;
  border-radius: 15px;
  box-shadow: 0 20px 20px 0 rgba(252, 169, 169, 0.6);
  display: flex;
  padding: 20px 30px;
  position: relative;
  margin: 100px 0;
  z-index: 10;
}

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

.img-container::after {
  content: '';
  background-color: #fff;
  border-radius: 50%;
  position: absolute;
  bottom: 100%;
  left: 50%;
  width: 20px;
  height: 20px;
  transform: translate(-50%, 50%);
}

.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;
}

.music-container.play .img-container img {
  animation-play-state: running;
}

@keyframes rotate {
  from {
    transform: rotate(0deg);
  }

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

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

.action-btn {
  background-color: #fff;
  border: 0;
  color: #dfdbdf;
  font-size: 20px;
  cursor: pointer;
  padding: 10px;
  margin: 0 20px;
}

.action-btn.action-btn-big {
  color: #cdc2d0;
  font-size: 30px;
}

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

.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;
}

.music-container.play .music-info {
  opacity: 1;
  transform: translateY(-100%);
}

.music-info h4 {
  margin: 0;
}

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

.progress {
  background-color: #fe8daa;
  border-radius: 5px;
  height: 100%;
  width: 0%;
  transition: width 0.1s linear;
}

در بالا برای المنت های خود استایل موردنظر را تعریف می کنیم. وقتی که آهنگ پخش می شود دوست داریم که تصویر کاور بچرخد. برای این کار باید از انیمیشن ها استفاده کنیم. برای انیمیشن باید یک نام تعریف کنیم و سپس keyframes آن را قابل اجرا کنیم. وضعیت انیمیشن paused یعنی متوقف شده است.

.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;
}

اگر تصویر یا نگهدارنده آن یعنی img-container دارای کلاس play باشد می خواهیم وضعیت انیمیشن به حالت اجرا یا running در بیاید.

.music-container.play .img-container img {
  animation-play-state: running;
}

@keyframes rotate {
  from {
    transform: rotate(0deg);
  }

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

چون می خواهیم تصویر یک چرخش کامل داشته باشد یا از صفر تا 360 درجه زاویه چرخش آن تغییر کند. هم چنین می خواهیم وقتی آهنگ پخش می شود کانتینر اطلاعات آهنگ به سمت بالا می آید و نمایش داده شود. پس تا هنگامی که آهنگ پخش نشده است نباید نمایش داده شود پس باید translateY باید صفر درصد باشد. transition به شما این امکان را می دهد که مقادیر property ها را به تدریج، در مدت زمان معینی تغییر دهید.

.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;
}

اما اگر آهنگ پخش شد باید اطلاعات نمایش داده شود پس translateY باید 100%- شود.

.music-container.play .music-info {
  opacity: 1;
  transform: translateY(-100%);
}

برای نشان دادن مدت زمان آهنگ از یک تگ div با کلاس progress-container استفاده می کنیم که در music-info تعریف شده است. برای نشان دادن اجرا شدن آهنگ و گذشتن زمان در css از کد زیر استفاده می کنیم:

.progress {
  background-color: #fe8daa;
  border-radius: 5px;
  height: 100%;
  width: 0%;
  transition: width 0.1s linear;
}

اگر برنامه را اجرا کنیم باید تصویر زیر را ببینیم:

اگر دکمه ها را فشار دهیم هیچ اتفاقی نمی افتد. برای کار کردن این برنامه به جاوااسکریپت نیاز داریم. پس بیایید آن را بسازیم.

فایل script.js

این فایل مغز برنامه است. کد آن در زیر آمده است:

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');

// Song titles
const songs = ['hey', 'summer', 'ukulele'];

// Keep track of song
let songIndex = 2;

// Initially load song details into DOM
loadSong(songs[songIndex]);

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

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

  audio.play();
}

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

  audio.pause();
}

// Previous song
function prevSong() {
  songIndex--;

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

  loadSong(songs[songIndex]);

  playSong();
}

// Next song
function nextSong() {
  songIndex++;

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

  loadSong(songs[songIndex]);

  playSong();
}

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

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

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

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

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

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

// Time/song update
audio.addEventListener('timeupdate', updateProgress);

// Click on progress bar
progressContainer.addEventListener('click', setProgress);

// Song ends
audio.addEventListener('ended', nextSong);

سپس باید آهنگ ها را تعریف کنیم.نام آهنگ ها را در آرایه ای قرار می دهیم.

// Song titles
const songs = ['hey', 'summer', 'ukulele'];

برای این که برنامه به درستی کار کند نام تصویرها باید با نام آهنگ ها یکسان باشد. برای ردیابی و در واقع دستیابی به آن ها باید از یک اندیس استفاده کنیم.

// Keep track of song
let songIndex = 2;

حالا باید آهنگ ها را واکشی کنیم و در فایل javascript خود قرار دهیم.

// Initially load song details into DOM
loadSong(songs[songIndex]);

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

تابع loadSong برای واکشی ها و قرار دادن آن ها در DOM است. این تابع نام آهنگ، مکان آهنگ و مکان تصویر آن را تنظیم می کند. می خواهیم وقتی روی دکمه play کلیک می کنیم آهنگ پخش شود. برای این کار یک شنونده رویداد به این دکمه می افزاییم.

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

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

اگر کانتینر دارای کلاس play باشد یعنی آهنگ باید اجرا شود. این تابع این شرط را بررسی می کند و در صورت برقرار بودن شرط تابع pauseSong و در غیر این صورت تابع playSong را اجرا می کنیم.

کد تابع pauseSong در زیر آمده است:

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

  audio.pause();
}

کد تابع playSong در زیر آمده است:

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

  audio.play();
}

با audio.play آهنگ پخش و با audio.pause آهنگ متوقف می شود. برای این که بتوانیم آهنگ را تغییر بدهیم و آهنگ بعدی یا قبلی برویم باید برای دکمه های next و pre شنونده رویداد ایجاد کنیم.

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

تابع prevSong برای پخش آهنگ قبلی است. آهنگ قبلی یک خانه یا یک اندیس با آهنگی که در حال پخش است تفاوت دارد. پس اندیس باید یک واحد کم شود. اگر این کم کردن باعث منفی شدن اندیس شود آخرین آهنگ پخش خواهد شد.کد آن در زیر آمده است:

// Previous song
function prevSong() {
  songIndex--;

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

  loadSong(songs[songIndex]);

  playSong();
}

تابع nextSong برای پخش آهنگ بعدی است. آهنگ بعدی یک خانه یا یک اندیس با آهنگی که در حال پخش است تفاوت دارد. پس اندیس باید یک اضافه شود. اگر اندازه اندیس از اندازه آرایه بیش تر شود اولین آهنگ پخش خواهد شد. کد آن در زیر آمده است:

// Next song
function nextSong() {
  songIndex++;

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

  loadSong(songs[songIndex]);

  playSong();
}

در ادامه دوست داریم با پخش شدن آهنگ و گذشت زمان نوار progress به روز آوری شود و پر شود. برای این کار داریم:

// Time/song update
audio.addEventListener('timeupdate', updateProgress);

برای پرشدن نوار progress تابع updateProgress را تعریف می کنیم:

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

طول زمانی آهنگ و زمان کنونی پخش آن را می توان از srcElement به دست آورد. از این اعداد برای محاسبه درصد پرشدن نوار progress استفاده می کنیم.درصد به دست آمده را در متغیر progressPercent قرار می دهیم. این عدد with یا طول نوار progress را مشخص می کند.

در ادامه می خواهیم با کلیک کردن روی نوار progress، آهنگ به جایی که کلیک کرده ایم برود و از آن جا پخش شود و هم چنین نوار تا آن جا رنگی شود. برای این کار یک شنونده رویداد به متغیر progressContainer اضافه می کنیم.

// Click on progress bar
progressContainer.addEventListener('click', setProgress);

با کلیک کردن بر روی نوار progress تابع setProgress اجرا می شود. کد این تابع در زیر آمده است:

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

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

به جایی که موس کلیک می شود نیاز داریم.جایی که موس کلیک می شود را در متغیر clickX قرار می دهیم. در آخر می خواهیم وقتی یک آهنگ تمام می شود، آهنگ بعدی پخش شود.

// Song ends
audio.addEventListener('ended', nextSong);
نویسنده شوید

دیدگاه‌های شما (1 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

مسیح
16 اسفند 1400
من همین 2 دقیقه پیش دربهدر دنبال همچین چیزی بودم :] یه لطفی میکنی کلش رو در اختارمون بزارید برای دانلود؟

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.