티스토리 뷰
파일구조
HTML
<div class="typing__wrap">
<input type="text" class="input__field">
<div class="typing__inner">
<div class="typing__text">
<p></p>
</div>
<div class="typing__info">
<ul>
<li class="time">
<p>Time Left:</p>
<span><b>60</b>s</span>
</li>
<li class="mistake">
<p>Mistakes: </p>
<span>0</span>
</li>
<li class="wpm">
<p>wpm: </p>
<span>0</span>
</li>
<li class="cpm">
<p>cpm: </p>
<span>0</span>
</li>
</ul>
<button class="again">Try Again</button>
</div>
</div>
</div>
CSS
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Poppins";
}
body {
background: #17A2B8;
}
.typing__wrap {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
}
.typing__inner {
width: 60vw;
min-width: 900px;
min-height: 40vh;
background: #fff;
border-radius: 20px;
padding: 40px;
}
.input__field {
position: absolute;
left: 10px;
top: 10px;
padding: 10px;
}
.typing__text {
border: 1px solid #C3C3C3;
border-radius: 10px;
padding: 30px;
min-height: 300px;
font-size: 22px;
font-weight: 500;
text-align: justify;
word-break: break-all;
}
.typing__text p span {
position: relative;
}
.typing__text p span.correct {
color: rgb(54, 20, 245);
}
.typing__text p span.incorrect {
color: #cd3439;
background: #ffc0cb;
outline: 1px solid #ffff;
border-radius: 4px;
}
.typing__text p span.active {
color: #17A2B8;
}
.typing__text span.active::before {
content: '';
position: absolute;
left: 0;bottom: 0;
width: 100%;
height: 2px;
background: #17A2B8;
animation: blink 1s ease-in-out infinite;
opacity: 0;
}
@keyframes blink {
50% {opacity: 1;}
}
.typing__info {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 30px;
}
.typing__info button {
width: 210px;
background: #17A2B8;
color:#fff;
font-family: "Poppins";
border: 0;
border-radius: 5px;
padding: 10px;
font-size: 22px;
margin-left: 20px;
cursor: pointer;
}
.typing__info ul {
display: flex;
width: 100%;
justify-content: space-around;
}
.typing__info ul li {
/* width: 20%; */
list-style: none;
display: flex;
font-size: 20px;
border-left: 1px solid #C3C3C3;
padding-left: 40px;
}
.typing__info ul li:first-child {
padding-left: 0;
border-left: 0;
}
.typing__info li p {
white-space: nowrap;
}
.typing__info li span {
padding: 0 10px;
}
SCRIPT
<script src="api/music.js"></script>
<script>
const musicWrap = document.querySelector(".wrap__music");
const musicImg = musicWrap.querySelector(".music__img img");
const musicName = musicWrap.querySelector(".music__song .name");
const musicArtist = musicWrap.querySelector(".music__song .artist");
const musicAudio = musicWrap.querySelector("#main-audio");
const musicPlay = musicWrap.querySelector("#control-play");
const musicPrevBtn = musicWrap.querySelector("#control-prev");
const musicNextBtn = musicWrap.querySelector("#control-next");
const musicProgress = musicWrap.querySelector(".music__progress");
const musicProgressBar = musicProgress.querySelector(".bar");
const musicProgressCurrent = musicProgress.querySelector(".current");
const musicProgressDuration = musicProgress.querySelector(".duration");
const musicRepect = musicWrap.querySelector("#control-repeat");
const musicList = musicWrap.querySelector(".music__list");
const musicListBtn = musicWrap.querySelector("#control-list");
const musicListClose = musicList.querySelector(".close");
const musicListUl = musicList.querySelector(".list ul");
let musicIndex = Math.floor((Math.random()*allMusic.length)+1); //현재 재생 음악 인덱스
//음악 재생
function loadMusic(num){
musicImg.src = `images/${allMusic[num-1].img}.jpg`;
musicImg.alt = allMusic[num-1].name;
musicName.innerText = allMusic[num-1].name;
musicArtist.innerText = allMusic[num-1].artist;
musicAudio.src = `songs/${allMusic[num-1].audio}.mp3`;
}
// 음악 플레이
function playMusic(){
musicWrap.classList.add("paused");
musicPlay.innerText = "pause";
musicPlay.setAttribute("title", "플레이") //속성 부여 메서드
musicAudio.play();
}
// 음악 정지
function pauseMusic(){
musicWrap.classList.remove("paused");
musicPlay.innerText = "play_arrow";
musicPlay.setAttribute("title", "정지") //속성 부여 메서드
musicAudio.pause();
}
// 이전 곡 재생
function pervMusic(){
musicIndex--;
musicIndex < 1 ? musicIndex = allMusic.length : musicIndex = musicIndex;
loadMusic(musicIndex);
playMusic();
playListMusic();
}
// 다음 곡 재생
function nextMusic(){
musicIndex++;
musicIndex > allMusic.length ? musicIndex = 1 : musicIndex = musicIndex;
loadMusic(musicIndex);
playMusic();
playListMusic();
}
// 뮤직 진행바
musicAudio.addEventListener("timeupdate", (e) => {
//console.log(e);
const currentTime = e.target.currentTime;
const duration = e.target.duration;
let progressWidth = (currentTime / duration) * 100;
musicProgressBar.style.width = `${progressWidth}%`;
musicAudio.addEventListener("loadeddata", () => {
let audioDuration = musicAudio.duration;
let totalMin = Math.floor(audioDuration / 60);
let totalSec = Math.floor(audioDuration % 60);
if(totalSec < 10) totalSec = `0${totalSec}`;
musicProgressDuration.innerText = `${totalMin}:${totalSec}`;
});
let currentMin = Math.floor(currentTime / 60);
let currentSec = Math.floor(currentTime % 60);
if( currentSec < 10) currentSec = `0${currentSec}`;
musicProgressCurrent.innerText =`${currentMin}:${currentSec}`;
});
// 플레이 버튼
musicPlay.addEventListener("click", () => {
const isMusicPaused = musicWrap.classList.contains("paused");
isMusicPaused ? pauseMusic() : playMusic();
playListMusic();
});
// 이전 곡 버튼
musicPrevBtn.addEventListener("click", () => {
pervMusic();
});
// 다음 곡 버튼
musicNextBtn.addEventListener("click", () => {
nextMusic();
});
// 진행 버튼 클릭
musicProgress.addEventListener("click", (e) => {
let progressWidth = musicProgress.clientWidth;
let clickedOffsetX = e.offsetX;
let songDuration = musicAudio.duration;
musicAudio.currentTime = (clickedOffsetX / progressWidth ) * songDuration; //musicAudio의 현재값의 클릭한 지점의 전체 길이값 ) *(곱하기) 노래Duration
playMusic();
})
//반복 버튼 : 3가지 토글 메뉴 만들기 위해 switch문으로 작성
musicRepect.addEventListener("click", () => {
let getText = musicRepect.innerText;
switch(getText){
case "repeat" :
musicRepect.innerText = "repeat_one";
musicRepect.setAttribute("title", "한곡 반복") //속성 부여 메서드
break;
case "repeat_one" :
musicRepect.innerText = "shuffle";
musicRepect.setAttribute("title", "랜덤 반복") //속성 부여 메서드
break;
case "shuffle" :
musicRepect.innerText = "repeat";
musicRepect.setAttribute("title", "전체 반복") //속성 부여 메서드
break;
}
});
//오디오가 끝나고
musicAudio.addEventListener("ended", () => { //ended 00하는 이벤트
let getText = musicRepect.innerText;
switch(getText){
case "repeat" :
nextMusic(); //'다음 곡 재생' 함수 실행
break;
case "repeat_one" :
loadMusic(musicIndex); //'현재 재생 음악 인덱스'를 다시 불러옴
playMusic(); //'음악 플레이' 함수 실행
playListMusic();
break;
case "shuffle" :
let randIndex = Math.floor((Math.random()*allMusic.length)+1);
do { //조건에 맞는 경우에만 실행
randIndex = Math.floor((Math.random()*allMusic.length)+1);
} while (musicIndex =+ randIndex); // 맞든 안 맞든 1번은 실행해줌
musicIndex = randIndex;
loadMusic(randIndex);
playMusic();
playListMusic();
break;
}
});
//뮤직 리스트 버튼
musicListBtn.addEventListener("click", () => {
musicList.classList.add("show");
});
// 뮤직 리스트 닫기 버튼
musicListClose.addEventListener("click", () => {
musicList.classList.remove("show");
});
//뮤직 리스트 구현하기 : musicList Ul에 리스트 삽입
for(let i = 0; i<allMusic.length; i++){
let li = `
<li data-index="${i+1}">
<div>
<em>${allMusic[i].name}</em>
<p>${allMusic[i].artist}</p>
</div>
<audio class="${allMusic[i].audio}" src="songs/${allMusic[i].audio}.mp3"></audio>
<span id="${allMusic[i].audio}" class="audio-duration">3:36</span>
</li>
`;
musicListUl.insertAdjacentHTML("beforeend", li); //musicListUl에 삽입
//지속시간 표기
let liAudioDuration = musicListUl.querySelector(`#${allMusic[i].audio}`);
let liAudio = musicListUl.querySelector(`.${allMusic[i].audio}`);
liAudio.addEventListener("loadeddata", () => { //뮤직 진행바에서 가져옴
let audioDuration = liAudio.duration;
let totalMin = Math.floor(audioDuration / 60);
let totalSec = Math.floor(audioDuration % 60);
if(totalSec < 10) totalSec = `0${totalSec}`;
liAudioDuration.innerText = `${totalMin}:${totalSec}`;
liAudioDuration.setAttribute("data-duration", `${totalMin}:${totalSec}`);
});
playListMusic();
}
//뮤직 리스트 클릭
function playListMusic() {
const musicListAll = musicListUl.querySelectorAll("li");
//현재 재생 음악에 불 들어오게하기
for(let j=0; j<musicListAll.length; j++){
let audioTag = musicListAll[j].querySelector(".audio-duration");
if(musicListAll[j].classList.contains("playing")){
musicListAll[j].classList.remove("playing");
let adDuration = audioTag.getAttribute("data-duration");
audioTag.innerText = adDuration;
}
if(musicListAll[j].getAttribute("data-index") == musicIndex){
musicListAll[j].classList.add("playing");
audioTag.innerText = "재생중";
}
musicListAll[j].setAttribute("onclick", "clicked(this)");
}
}
// 리스트 클릭하기
function clicked(el){
let getLiIndex = el.getAttribute("data-index");
musicIndex = getLiIndex;
loadMusic(musicIndex);
playMusic();
playListMusic();
}
// 플레이
window.addEventListener("load", () => {
loadMusic(musicIndex);
playListMusic();
});
</script>
music.js
let allMusic = [
{
name : "1.노래제목",
artist : "아티스트",
img : "music-1(이미지 파일명)",
audio : "music-1(음악 파일명)"
}
}
댓글
© 2018 webstoryboy