<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Music Player</title>
    <link rel="icon" type="image/png" href="favicon.png">
    <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">
</head>
<body>
    <div class="player-container">
        <!-- Song -->
        <div class="img-container">
            <img src="img/jacinto-1.jpg" alt="Album Art">
        </div>
        <h2 id="title">Electric Chill Machine</h2>
        <h3 id="artist">Jacinto</h3>
        <audio src="music/jacinto-1.mp3"></audio>
        <!-- Progress -->
        <div class="progress-container" id="progress-container">
            <div class="progress" id="progress"></div>
            <div class="duration-wrapper">
                <span id="current-time">0:00</span>
                <span id="duration">2:06</span>
            </div>
        </div>
        <!-- Controls -->
        <div class="player-controls">
            <i class="fas fa-backward" id="prev" title="Previous"></i>
            <i class="fas fa-play main-button" id="play" title="Play"></i>
            <i class="fas fa-forward" id="next" title="Next"></i>
        </div>
    </div>
    <!-- Script -->
    <script src="script.js"></script>
</body>
</html>
/* Images created by: https://unsplash.com/@pawel_czerwinski */

@import url("https://fonts.googleapis.com/css?family=Spartan:400,700&display=swap");

html {
  box-sizing: border-box;
}

body {
  margin: 0;
  min-height: 100vh;
  background: #abdfd4;
  display: flex;
  justify-content: center;
  align-items: center;
  font-family: Spartan, sans-serif;
  font-size: 12px;
}

.player-container {
  height: 500px;
  width: 400px;
  background: #dcefde;
  border-radius: 20px;
  box-shadow: 0 15px 30px 5px rgba(0, 0, 0, 0.3);
}

.img-container {
  width: 300px;
  height: 300px;
  position: relative;
  top: -50px;
  left: 50px;
}

.img-container img {
  height: 100%;
  width: 100%;
  object-fit: cover;
  border-radius: 20px;
  box-shadow: 0 5px 30px 5px rgba(0, 0, 0, 0.5);
}

h2 {
  font-size: 25px;
  text-align: center;
  margin: 0;
}

h3 {
  font-size: 20px;
  text-align: center;
  font-weight: 400;
  margin: 5px 0 0;
}

/* Progress */
.progress-container {
  background: #fff;
  border-radius: 5px;
  cursor: pointer;
  margin: 40px 20px;
  height: 4px;
  width: 90%;
}

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

.duration-wrapper {
  position: relative;
  top: -25px;
  display: flex;
  justify-content: space-between;
}

/* Controls */
.player-controls {
  position: relative;
  top: -15px;
  left: 120px;
  width: 200px;
}

.fas {
  font-size: 30px;
  color: rgb(129, 129, 129);
  margin-right: 30px;
  cursor: pointer;
  user-select: none;
}

.fas:hover {
  filter: brightness(80%);
}

.main-button {
  font-size: 40px;
  position: relative;
  top: 3px;
}

/* Media Query: iPhone (Vertical) */
@media screen and (max-width: 376px) {
  .player-container {
    width: 95vw;
  }

  .img-container {
    left: 29px;
  }

  h2 {
    font-size: 20px;
  }

  h3 {
    font-size: 15px;
  }

  .player-controls {
    top: -10px;
    left: 100px;
  }
}
const image = document.querySelector('img');
const title = document.getElementById('title');
const artist = document.getElementById('artist');
const music = document.querySelector('audio');
const currentTimeEl = document.getElementById('current-time');
const durationEl = document.getElementById('duration');
const progress = document.getElementById('progress');
const progressContainer = document.getElementById('progress-container');
const prevBtn = document.getElementById('prev');
const playBtn = document.getElementById('play');
const nextBtn = document.getElementById('next');

// Music
const songs = [
  {
    name: 'jacinto-1',
    displayName: '아이스크림 협곡 대탐험!',
    artist: '녹차',
  },
  {
    name: 'jacinto-2',
    displayName: '협곡의 초코볼 거인',
    artist: '초코',
  },
  {
    name: 'jacinto-3',
    displayName: '위기의 녹차맛 폭풍!!',
    artist: '딸기',
  },
  {
    name: 'metric-1',
    displayName: '신비의 딸기 숲',
    artist: '바닐라',
  },
];

// 음악이 실행되는가??
let isPlaying = false;

// Play
function playSong() {
  isPlaying = true;
  playBtn.classList.replace('fa-play', 'fa-pause');
  playBtn.setAttribute('title', 'Pause');
  music.play();
}

// Pause
function pauseSong() {
  isPlaying = false;
  playBtn.classList.replace('fa-pause', 'fa-play');
  playBtn.setAttribute('title', 'Play');
  music.pause();
}

// Play or Pause Event Listener
playBtn.addEventListener('click', () => (isPlaying ? pauseSong() : playSong()));

// Update DOM
function loadSong(song) {
  title.textContent = song.displayName;
  artist.textContent = song.artist;
  music.src = `music/${song.name}.mp3`;
  image.src = `img/${song.name}.jpg`;
}

// 현재 곡
let songIndex = 0;

// 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();
}

//맨처음 로드시에 첫곡연주.
loadSong(songs[songIndex]);

// Update Progress Bar & Time
function updateProgressBar(e) {
  if (isPlaying) {
    const { duration, currentTime } = e.srcElement;
    //노래의 전체길이, 현재시간을 객체로서 전달받는다. (구조분해할당.)

    // 진행바 길이 업데이트
    const progressPercent = (currentTime / duration) * 100;
    progress.style.width = `${progressPercent}%`;
    //퍼센티지로 표현.

    // 노래의 전체 시간(2:0x)을 계산하기.
    const durationMinutes = Math.floor(duration / 60);
    let durationSeconds = Math.floor(duration % 60);
    if (durationSeconds < 10) {
      durationSeconds = `0${durationSeconds}`;
    }
    //초가 10초 미만이명 0X 이런형식으로 표현하기.

    // 그냥 표기하면 일순간 분초가 NAN으로 표시된다. 정보를 전달받는 첫과정이 실행되지 못해 값이 없는 것. 이걸 해결하기 위해 조건문 사용.    
    if (durationSeconds) {
      durationEl.textContent = `${durationMinutes}:${durationSeconds}`;
    }//초가 있어야만 시간이 표기된다.

    // 현재 시간을 계산.
    const currentMinutes = Math.floor(currentTime / 60);
    let currentSeconds = Math.floor(currentTime % 60);
    if (currentSeconds < 10) {
      currentSeconds = `0${currentSeconds}`;
    }
    currentTimeEl.textContent = `${currentMinutes}:${currentSeconds}`;
  }
}

// 진행률 바 설정.
function setProgressBar(e) {
  const width = this.clientWidth;
  const clickX = e.offsetX;
  const { duration } = music;
  music.currentTime = (clickX / width) * duration;
}

// Event Listeners
prevBtn.addEventListener('click', prevSong);
nextBtn.addEventListener('click', nextSong);
music.addEventListener('ended', nextSong);
music.addEventListener('timeupdate', updateProgressBar);
progressContainer.addEventListener('click', setProgressBar);
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.