https://taehyunkim3.github.io/JS30/drum.html
왜 Bootstrap demo 제목이 안바뀌는지 모르겠다. 아마 github 혹은 티스토리 자체 캐쉬가 있나..?
소리가 좀 거슬릴것 같아서 음소거 버튼을 제작해서 넣었다.
클릭할때마다 muted라는 변수가 false인 경우에만 sound.play함수를 실행한다.
muted변수를 그냥 전역에다가 놔뒀는데, 뭔가 풍파에 노출시켜둔 뭔가 찝찝한 기분이라 찾아보니
클로저라는 뭔가 어려운데 필요해보이는 개념이 있었다.
요거는 조금만 더 공부해보고 적용볼 예정.
https://codepen.io/arlekino/pen/DEBeRE
아주 간단한 버튼인데, 부트스트랩을 사용하지 않고 제작하고 싶어서 여기서 요소를 따와서 제작했다.
해결해야 할 문제
1. 마우스 클릭 이벤트와 키보드 이벤트가 따로 되어있었는데, 키보드만 수정하고 안되서 한참 찾았다.
JS와 CSS파일을 분리해서 좀더 깔끔하게 정리를 해야할것 같음. 코드가 너무 지저분함..
-> 원인중 하나가 원래 마우스 기능은 없어서 soundplay라는 함수만 있었는데, 나중에 mouseclick이라는 거의 똑같은 역할을 수행하는 함수가 추가되었다.
->soundplay랑 mouseclick 두 함수의 중복되는 부분을 하나로 합치고, 정리를 해줘야겠다.
2. 버튼이 지금도 간헐적으로 동작하지 않는다. 해결해야함.
html 버튼부분
<p class="button-text">SOUND</p>
<label class="switch" id="switch">
<input type="checkbox" onclick="mute(this)" />
</label>
css 버튼부분
.button-text {
position: fixed;
top: 10px;
right: 100px;
color: white;
}
.switch {
position: fixed;
top: 20px;
right: 20px;
display: inline-block;
width: 60px;
height: 30px;
background-color: rgba(105, 105, 105, 0.4);
border: 1px solid rgba(0, 0, 0, 0.4);
border-radius: 20px;
transition: all 200ms;
}
input[type="checkbox"] {
appearance: none;
width: 24px;
height: 24px;
background-color: hsl(151, 68%, 47%);
border-radius: 50%;
transition: all 200ms;
}
.switch:active input[type="checkbox"] {
width: 34px;
}
.switch:active input[type="checkbox"]:checked {
width: 34px;
margin-left: -8px;
}
input[type="checkbox"]:focus {
outline: none;
}
input[type="checkbox"]:checked {
transform: translateX(30px);
background-color: hsl(0, 1%, 54%);
}
버튼이랑 동작 JS부분.
let muted = false;
function mute(checkbox) {
muted = checkbox.checked;
console.log(muted);
}
function playSound(e) {
const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
const key = document.querySelector(`.key[data-key="${e.keyCode}"]`);
const keyWrap = document.querySelector(
`.key-wrap[data-key="${e.keyCode}"]`
);
const keyBack = document.querySelector(
`.key[data-key='${e.keyCode}']>div`
);
if (!audio) return;
audio.currentTime = 0;
console.log(muted);
if (!muted) {
audio.play();
}
keyWrap.classList.toggle("wrap-hit");
keyBack.classList.toggle("hit-back");
}
function mouseClick() {
const keyCode = parseInt(this.dataset.key);
const audio = document.querySelector(`audio[data-key="${keyCode}"]`);
const key = document.querySelector(`.key[data-key="${keyCode}"]`);
const keyWrap = document.querySelector(
`.key-wrap[data-key="${keyCode}"]`
);
const keyBack = document.querySelector(
`.key[data-key='${keyCode}']>div`
);
if (!audio) return;
audio.currentTime = 0;
if (!muted) {
audio.play();
}
keyWrap.classList.toggle("wrap-hit");
keyBack.classList.toggle("hit-back");
}
아래는 전체 코드
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Javascript Project 30 응용</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=" />
<style>
@import url("https://fonts.googleapis.com/css2?family=Arvo:ital,wght@0,400;0,700;1,400;1,700&display=swap");
</style>
<style>
* {
font-family: "Arvo", serif;
}
body {
margin: 0;
}
.wrapper {
height: 100vh;
background-size: cover;
background-image: url("https://cdn.pixabay.com/photo/2018/05/10/11/34/concert-3387324_1280.jpg");
display: flex;
flex-wrap: wrap;
align-content: center;
}
.button-text {
position: fixed;
top: 10px;
right: 100px;
color: white;
}
.switch {
position: fixed;
top: 20px;
right: 20px;
display: inline-block;
width: 60px;
height: 30px;
background-color: rgba(105, 105, 105, 0.4);
border: 1px solid rgba(0, 0, 0, 0.4);
border-radius: 20px;
transition: all 200ms;
}
input[type="checkbox"] {
appearance: none;
width: 24px;
height: 24px;
background-color: hsl(151, 68%, 47%);
border-radius: 50%;
transition: all 200ms;
}
.switch:active input[type="checkbox"] {
width: 34px;
}
.switch:active input[type="checkbox"]:checked {
width: 34px;
margin-left: -8px;
}
input[type="checkbox"]:focus {
outline: none;
}
input[type="checkbox"]:checked {
transform: translateX(30px);
background-color: hsl(0, 1%, 54%);
}
@media (min-width: 915px) {
.wrapper {
justify-content: space-around;
}
}
.key {
height: 80px;
width: 80px;
background-color: rgba(0, 0, 0, 0.2);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
transition: all 15d0ms;
transform: rotateX(20deg) rotateZ(20deg);
transform-style: preserve-3d;
box-shadow: 5px 5px 20px -5px #444;
}
.key:before {
content: "";
position: absolute;
height: 40px;
width: 80px;
top: 80px;
left: 0px;
transform: rotateX(90deg) translateX(2px) translateY(-8.5px)
translateZ(16px);
background-color: rgba(0, 0, 0, 0.2);
pointer-events: none;
}
.key:after {
content: "";
position: absolute;
height: 80px;
width: 40px;
top: 2px;
right: -36px;
transform: rotateY(90deg) translateZ(-15px) translateX(12px)
translateX(0px);
border-width: 0;
border-style: solid;
border-color: #fff;
background-color: rgba(0, 0, 0, 0.2);
pointer-events: none;
}
.key .kbd {
color: white;
font-size: 20px;
}
.key-wrap {
margin: 20px;
display: block;
transition: all 200ms;
}
.wrap-hit {
transform: translateY(-20px);
}
.key-back.hit-back {
box-shadow: 0px 0px 56px 22px rgba(247, 247, 0, 1);
border: 2px solid white;
}
.sound {
color: yellow;
font-size: 10px;
}
.key-back {
position: absolute;
background-color: rgba(0, 0, 0, 0.2);
width: 80px;
height: 80px;
transform: translateZ(-40px);
box-shadow: 10px 10px 50px 0px #111;
transition: all 150ms;
}
</style>
</head>
<body>
<main class="wrapper">
<p class="button-text">SOUND</p>
<label class="switch" id="switch">
<input type="checkbox" onclick="mute(this)" />
</label>
<div class="key-wrap" data-key="65">
<div data-key="65" class="key">
<span class="kbd">A</span>
<span class="sound">click</span>
<div class="key-back"></div>
</div>
</div>
<div data-key="83" class="key-wrap">
<div data-key="83" class="key">
<span class="kbd">S</span>
<span class="sound">click2</span>
<div class="key-back"></div>
</div>
</div>
<div data-key="68" class="key-wrap">
<div data-key="68" class="key">
<span class="kbd">D</span>
<span class="sound">keyboard</span>
<div class="key-back"></div>
</div>
</div>
<div data-key="70" class="key-wrap">
<div data-key="70" class="key">
<span class="kbd">F</span>
<span class="sound">metalic</span>
<div class="key-back"></div>
</div>
</div>
<div data-key="71" class="key-wrap">
<div data-key="71" class="key">
<span class="kbd">G</span>
<span class="sound">moneybag</span>
<div class="key-back"></div>
</div>
</div>
<div data-key="72" class="key-wrap">
<div data-key="72" class="key">
<span class="kbd">H</span>
<span class="sound">perc</span>
<div class="key-back"></div>
</div>
</div>
<div data-key="74" class="key-wrap">
<div data-key="74" class="key">
<span class="kbd">J</span>
<span class="sound">coin</span>
<div class="key-back"></div>
</div>
</div>
<div data-key="75" class="key-wrap">
<div data-key="75" class="key">
<span class="kbd">K</span>
<span class="sound">duck</span>
<div class="key-back"></div>
</div>
</div>
<div data-key="76" class="key-wrap">
<div data-key="76" class="key">
<span class="kbd">L</span>
<span class="sound">button</span>
<div class="key-back"></div>
</div>
</div>
<div data-key="186" class="key-wrap">
<div data-key="186" class="key">
<span class="kbd">;</span>
<span class="sound">bell</span>
<div class="key-back"></div>
</div>
</div>
</main>
<audio data-key="65" src="sound/click-button-140881.mp3"></audio>
<audio data-key="83" src="sound/click-button-menu-147349.mp3"></audio>
<audio data-key="68" src="sound/mech-keyboard-02-102918.mp3"></audio>
<audio data-key="70" src="sound/metalic-groan-98230.mp3"></audio>
<audio data-key="71" src="sound/money-bag-82960.mp3"></audio>
<audio data-key="72" src="sound/perc03-44836.mp3"></audio>
<audio data-key="74" src="sound/silver-quarter-4-44684.mp3"></audio>
<audio data-key="75" src="sound/single-quack-from-a-duck-14494.mp3"></audio>
<audio
data-key="76"
src="sound/tape-machine-button-press-metal-84484.mp3"
></audio>
<audio data-key="186" src="sound/thin-bell-ding-4-83140.mp3"></audio>
<script>
window.addEventListener("keydown", playSound);
let muted = false;
function mute(checkbox) {
muted = checkbox.checked;
// checkbox.checked? muted=true : muted=false;
console.log(muted);
}
function playSound(e) {
const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
const key = document.querySelector(`.key[data-key="${e.keyCode}"]`);
const keyWrap = document.querySelector(
`.key-wrap[data-key="${e.keyCode}"]`
);
const keyBack = document.querySelector(
`.key[data-key='${e.keyCode}']>div`
);
if (!audio) return;
audio.currentTime = 0;
if (!muted) {
audio.play();
}
keyWrap.classList.toggle("wrap-hit");
keyBack.classList.toggle("hit-back");
}
function mouseClick() {
const keyCode = parseInt(this.dataset.key);
const audio = document.querySelector(`audio[data-key="${keyCode}"]`);
const key = document.querySelector(`.key[data-key="${keyCode}"]`);
const keyWrap = document.querySelector(
`.key-wrap[data-key="${keyCode}"]`
);
const keyBack = document.querySelector(
`.key[data-key='${keyCode}']>div`
);
if (!audio) return;
audio.currentTime = 0;
if (!muted) {
audio.play();
}
keyWrap.classList.toggle("wrap-hit");
keyBack.classList.toggle("hit-back");
}
//해당 동작이 끝나면 돌아가는 함수
function listenTransition(a) {
a.forEach((b) => {
b.addEventListener("transitionend", removeTransition);
});
}
function listenMouse(m) {
m.forEach((o) => {
o.addEventListener("click", mouseClick);
});
}
const keys = document.querySelectorAll(".key");
listenTransition(keys);
const keyBack = document.querySelectorAll(`.key > div`);
listenTransition(keyBack);
const keyWrap = document.querySelectorAll(`.key-wrap`);
listenTransition(keyWrap);
listenMouse(keyWrap);
function removeTransition(e) {
this.classList.remove("wrap-hit");
this.classList.remove("hit-back");
}
</script>
</body>
</html>