<!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);
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.