<html>
<head>
<meta charset="utf-8" />
<title>가위바위보</title>
<style>
#computer {
width: 142px;
height: 200px;
}
</style>
</head>
<body>
<div id="computer"></div>
<div>
<button id="scissors" class="btn">가위</button>
<button id="rock" class="btn">바위</button>
<button id="paper" class="btn">보</button>
</div>
<div id="score">0</div>
<script>
const $computer = document.querySelector('#computer');
const $score = document.querySelector('#score');
const $rock = document.querySelector('#rock');
const $scissors = document.querySelector('#scissors');
const $paper = document.querySelector('#paper');
const IMG_URL = './rsp.png';
$computer.style.background = `url(${IMG_URL}) -464px 0`;
$computer.style.backgroundSize = 'auto 200px';
...
</script>
</body>
</html>
이미지 파일이 가위바위보가 하나로 합쳐져 있는데, 인터넷 익스플로러만해도 한번에 가져올 수 있는 파일의 개수가 제한이 있어 이렇게 했다. 가져와서 background px을 옮겨서 잘라서 쓴다.
const rspX = {
scissors: '0', // 가위
rock: '-220px', // 바위
paper: '-440px', // 보
};
객체를 이용함으로서 그룹화를 했다. 각 이미지의 좌표를 나타내기 위함이다.
const rspX = {
scissors: '0', // 가위
rock: '-220px', // 바위
paper: '-440px', // 보
};
let computerChoice = 'scissors';
const changeComputerHand = () => {
if (computerChoice === 'scissors') { // 가위면
computerChoice = 'rock';
} else if (computerChoice === rspX.rock) {
computerChoice = 'paper';
} else {
computerChoice = 'scissors';
}
$computer.style.background = 'url(${IMG_URL}) ${rspX[computerChoice]} 0';
$computer.style.backgroundSize = 'auto 200px';
}
setInterval(changeComputerHand, 50);
참고로 rspX.computerChoice라하면 안된다. rspX.computerChoice는 rspX["computerChoice"]와 같다. []안에는 값이 들어가야하고 점 뒤에는 문자열 자리다.
setInterval은 setTimeout으로 대체가 가능하다. 재귀적으로 구현하면.
setInterval(() => {
console.log("hello");
}, 1000);
function hello() {
console.log("hello");
setTimeout(hello, 1000);
}
setTimeout(hello, 1000);
둘은 거의 비슷하지만 약간 다르다. 아래 재귀는 만약 console.log 부분이 시간이 많이 걸리는 거라면 1초 보장이 더 안될 수도 있다.
let 아이디 = setInterval(함수, 밀리초);
clearInterval(아이디);
setInterval 함수는 반환값이 있다. 반환값은 타이머에 대한 아이디(숫자)로, 나중에 이 값을 이용해 타이머를 제거할 수 있다. 마찬가지로 setTimeout 함수도 clearTimeout 함수로 취소할 수 있다. 단, setTimeout 함수에 인수로 넣은 함수가 실행되기 전에 clearTimeout을 호출해야 한다.
let intervalId = setInterval(changeComputerHand, 50);
const clickButton = () => {
clearInterval(intervalid);
// 점수 계산 및 화면 표시
setTimeout(() => {
intervalId = setInterval(changeComputerHand, 50);
}, 1000);
};
$rock.addEventListener('click', clickButton);
$scissors.addEventListener('click', clickButton);
$paper.addEventListener('click', clickButton);
타이머마다 아이디가 달라지기 때문에, 달라지는 아이디를 항상 변수에 저장해놔야한다.
현재 clickButton 다섯 번 호출하면, setTimeout 다섯번 호출되고, setInterval도 다섯번 호출된다. 인터벌 1번, 2번, 3번, 4번, 5번 (얘만 interValid), 그 다음에 버튼을 클릭하면 5번만 취소.
const clickButton = () => {
clearInterval(intervalid);
// 점수 계산 및 화면 표시
setTimeout(() => {
clearInterval(intervalid);
intervalId = setInterval(changeComputerHand, 50);
}, 1000);
};
clearInterval이 setTimeout안에도 들어가서 코드 중복이라 생각할 수 있지만, 비동기기 때문에 setTimeout은 1초뒤에 실행된다. 중복이 아니다.
지금 같은 버튼을 연타하면 생기는 문제를 해결하는 중인데, 아예 버튼을 클릭하지 못하게 할 수도 있다. removeEventListener를 이용하면 된다. 그리고 1초 뒤에 다시 버튼을 클릭하게 할 수 있다.
또는 clickable이라는 변수를 만들어서 if문으로 1초뒤에 true로 만들어주는 방식을 이용할 수도 있다. 이벤트 리스너를 추가했다가 제거했다를 반복하는 과정에 문제가 생길 수도 있다.
let clickable = true;
const clickButton = () => {
if (clickable) {
clearInterval(intervalId);
clickable = false;
// 점수 계산 및 화면 표시
setTimeout(() => {
clickable = true;
intervalId = setInterval(changeComputerHand, 50);
}, 1000);
}
};
removeEventListener가 안먹히는 예제를 보자
const fun = (값) => () => {
console.log('고차함수입니다', 값);
}
fun(1) === fun(1)
// 태그.addEventListener('click', fun(1));
// 태그.removeEventListener('click', fun(1));
이 경우 remove가 안되는데, addEventListener에 인자로 들어가는 함수랑 removeEventListener에 인자로 들어가는 함수랑 같아야 하낟. 하지만 fun(1)===fun(1)
이 아니다. 함수도 객체이기 때문이다. 객체는 변수에 넣어서 참조해줘야 같다고 나온다. 따라서 아래는 참이다.
const a = {};
const b= a;
a === b; //true
const fun1 = func(1);
fun1 === fun1; //true
따라서 아래와 같이 해야 한다.
const fun = (값) => () => {
console.log('고차함수입니다', 값);
}
const fun1 = func(1);
태그.addEventListener('click', fun1);
태그.removeEventListener('click', fun1);
const scoreTable = {
rock: 0,
scissors: 1,
paper: -1,
};
// clickButton 5번 호출, 인터벌 1번, 2번, 3번, 4번, 5번(얘만 intervalId)
// 그 다음에 버튼을 클릭하면 5번만 취소
let clickable = true;
let score = 0;
const clickButton = () => {
if (clickable) {
clearInterval(intervalId);
clickable = false;
// 점수 계산 및 화면 표시
const myChoice = event.target.textContent === '바위'
? 'rock'
: event.target.textContent === '가위'
? 'scissors'
: 'paper';
const myScore = scoreTable[myChoice];
const computerScore = scoreTable[computerChoice];
const diff = myScore - computerScore;
let message;
// 2, -1은 승리조건이고, -2, 1은 패배조건, 점수표 참고
if ([2, -1].includes(diff)) {
score += 1;
message = '승리';
} else if ([-2, 1].includes(diff)) {
score -= 1;
message = '패배';
} else {
message = '무승부';
}
$score.textContent = `${message} 총: ${score}점`;
setTimeout(() => {
clickable = true;
intervalId = setInterval(changeComputerHand, 50);
}, 1000);
}
};
$rock.addEventListener('click', clickButton);
$scissors.addEventListener('click', clickButton);
$paper.addEventListener('click', clickButton);