[Udemy]20 Web Projects With Vanilla JavaScript -Project#10

HTML&CSS&Javascript · 2020. 12. 30. 11:50

유데미에서 Brad Traversy의 20 Web Projects With Vanilla JavaScript

를 하나씩 직접 코딩해보면서 정리하기 위한 글이다.

 

 

데모 페이지

vanillawebprojects.com/projects/music-player/

코드 링크

github.com/rshak8912/20-Web-Projects

 

 

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <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>뮤직 플레이어</h1>
    <div class="music-container" id="music-container">
        <div class="music-info">
            <h4 id="title">ukulele</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>

 

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: #ffffff;
    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::after {
    content: '';
    background-color: #fff;
    border-radius: 50%;
    position: absolute;
    bottom: 100%;
    left: 50%;
    width: 20px;
    height: 20px;
    transform: translate(-50%, 50%);

}

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

}

.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: #ffffff;
    border-radius: 5px;
    cursor: pointer;
    margin: 10px 0;
    height: 4px;
    width: 100%;

}

.progress {
    background-color: #fe8daa;
    border-radius: 5px;
    height: 100%;
    width: 40%;
    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');

const songs = ['hey', 'summer', 'ukulele'];

let songIndex = 2;

loadSong(songs[songIndex]);

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

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

    audio.play();
}

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

    audio.pause();
}

function prevSong() {
    songIndex--;

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

    loadSong(songs[songIndex]);

    playSong();
}

function nextSong() {
    songIndex++;

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

    loadSong(songs[songIndex]);

    playSong();
}

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

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

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

강의를 통해 배운 점(생각, 내용 정리)

 

  • 오디오 태그를 사용한 음악 파일 재생법과 스탑, 퍼즈, 플레이 등의 기능 구현을 해볼 수 있었음.