캐릭터메신저구현기 부드러운 무빙 - requestAnimationFrame
캐릭터 이동시 방향키로 이동을 하는데
누르고 있을 경우에도 무빙을 원했다.
keyDown, keyUp을 이용해
움직임 유지를 결정하고
painting이 다시 일어날 때마다
특정함수(캐릭터의움직임)을 계속해주는 requestAnimationFrame
setInerval 같은 녀석을 사용해본다.
1. 구현
<html>
...생략
</html>
<script>
const person = document.querySelector('.person');
const p1 = new Person(person);
document.addEventListener('keydown', (e) => {
p1.move(e.key);
});
document.addEventListener('keyup', (e) => {
p1.clearKey();
})
</script>
class Person {
...생략
move(key) {
this.key = key;
if (!this.key) cancelAnimationFrame(move);
switch (key) {
case 'ArrowUp' :
this.setTop(-10);
this.target.style.top = this.getTop();
break;
case 'ArrowDown' :
this.setTop(10);
this.target.style.top = this.getTop();
break;
case 'ArrowLeft' :
this.setLeft(-10);
this.target.style.left = this.getLeft();
break;
case 'ArrowRight' :
this.setLeft(10);
this.target.style.left = this.getLeft();
break;
default:
break;
}
requestAnimationFrame(move);
}
}
구현을 보면
keyDown에 move를 걸어놨다.
move 안에서 requestAnimationFrame을 호출하게 되는데
이렇게 되면 버튼을 누르고 있으면
move가 계속 호출되어
'requestAnimationFrame'도 계속 호출하게 된다.
의미가 없어진다.
painting이 일어나면서 requestAnimationFrame이 감지한 뒤 메소드를 실행시켜줘야하는데
이렇게 소스를 짜면
강제로 메소드를 실행시킨다..
움직이도 끊겨서 동작한다.
2. 수정된 전체 소스
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="./index.css">
</head>
<body>
<div class="background">
<div class="content">
<img src="./images//habboman.png" alt="" class="person">
<img src="./images/object1.png" alt="" class="obj object1">
<img src="./images/object2.png" alt="" class="obj object2">
</div>
</div>
</body>
</html>
<script src="./mixin.js"></script>
<script>
const person = document.querySelector('.person');
const p1 = new Person(person);
document.addEventListener('keydown', (e) => {
p1.setKey(e.key);
});
document.addEventListener('keyup', (e) => {
p1.clearKey();
})
p1.move();
</script>
class Person {
constructor(domNode) {
const PARENT = domNode.parentNode
this.key = null;
this.target = domNode;
this.top = this.target.offsetTop;
this.left = this.target.offsetLeft;
this.down = PARENT.clientHeight - (domNode.offsetTop + domNode.offsetHeight);
this.right = PARENT.clientWidth - (domNode.offsetLeft + domNode.offsetWidth);
}
getTop() {
return this.top + 'px';
}
getDown() {
return this.down + 'px';
}
getRight() {
return this.right + 'px';
}
getLeft() {
return this.left + 'px';
}
setTop(top) {
if (this.top <= -10 && top < 0) return; // 상단 끝
if (this.top >= 710 && top > 0) return; // 단 끝
if (top.includes?.('px')) {
top = top.replace('px', '');
}
this.top += top;
}
setDown(down) {
if (down.includes?.('px')) {
down = down.replace('px', '');
}
this.down += down;
}
setRight(right) {
if (right.includes?.('px')) {
right = right.replace('px', '');
}
this.right += right;
}
setLeft(left) {
if (this.left <= -23 && left < 0) return; // 왼쪽 끝
if (this.left >= 667 && left > 0) return; // 오른쪽 끝
if (left.includes?.('px')) {
left = left.replace('px', '');
}
this.left += left;
}
clearKey() {
this.key = null;
}
setKey(key) {
console.log(key);
this.key = key;
}
move() {
if (!this.key) cancelAnimationFrame(window.raf);
switch (this.key) {
case 'ArrowUp' :
this.setTop(-10);
this.target.style.top = this.getTop();
break;
case 'ArrowDown' :
this.setTop(10);
this.target.style.top = this.getTop();
break;
case 'ArrowLeft' :
this.setLeft(-10);
this.target.style.left = this.getLeft();
break;
case 'ArrowRight' :
this.setLeft(10);
this.target.style.left = this.getLeft();
break;
default:
break;
}
window.raf = requestAnimationFrame(this.move);
}
}
이렇게 수정하는 경우
requestAnimationFrame(this.move)를 할 경우
this.key를 못 찾는다.
따라서
스크립트 부분은 다음과 같이 변경해야한다.
move() {
if (!this.key) cancelAnimationFrame(window.raf);
switch (this.key) {
case 'ArrowUp' :
this.setTop(-10);
this.target.style.top = this.getTop();
break;
case 'ArrowDown' :
this.setTop(10);
this.target.style.top = this.getTop();
break;
case 'ArrowLeft' :
this.setLeft(-10);
this.target.style.left = this.getLeft();
break;
case 'ArrowRight' :
this.setLeft(10);
this.target.style.left = this.getLeft();
break;
default:
break;
}
window.raf = requestAnimationFrame(this.move.bind(this));
}
requestAnimationFrame은 window 함수이기 때문에
Person 클래스 내부의 this 에 붙은 필드값을 못찾는다.
그래서 bind 함수를 이용해
저 함수를 Person에 붙여서 사용할 수 있도록 처리하면 된다..