canvas를 사용하다보면 마우스 click, hover와 같은 이벤트를 적용하고 싶을때가 있다 이럴때 canvas는 HTML의 DOM요소처럼 처리가 되지 않기때문에 브라우저의 개발자 도구 요소탭에서도 선택이 불가능한데, 그럼 어떻게 캔버스로 그려진 요소에 이벤트를 부여할 수 있을까?
답은 다음과 같다.
만약에 canvas에 상자를 그리고 상자를 클릭하고 싶으면 canvas의 전체에 클릭 이벤트를 주고 클릭한 좌표와 그려진 상자 영역이 겹치는지를 확인하고 겹치면 클릭처리하도록 진행하면된다.
자세한 내용은 실습 코드를 보자.
<!DOCTYPE html>
<html>
<head>
<title>Canvas</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
canvas {
background: #eee;
}
</style>
</head>
<body>
<h1>Interaction</h1>
<canvas class="canvas" width="600" height="400"></canvas>
<script>
const canvas = document.querySelector("canvas");
const context = canvas.getContext("2d");
context.fillRect(200, 200, 100, 100);
const clickHandler = (event) => {
const { offsetX: x, offsetY: y } = event;
// 사격형 100 사이즈 내 클릭처리
if (x > 200 && x < 300 && y > 200 && y < 300) {
console.log("click!");
}
};
canvas.addEventListener("click", clickHandler);
</script>
</body>
</html>
코드를 보면 canvas의 전체에 클릭이벤트 함수 clickHandler
를 바인딩하였고 사각형 내에서 클릭을 했을때 "click!"이라는 문구를 출력하게 처리했다.
<script>
const canvas = document.querySelector("canvas");
const context = canvas.getContext("2d");
const boxes = [];
const mousePosition = { x: 0, y: 0 };
context.font = "bold 30px sans-serif";
class Box {
constructor(index, x, y, width, height) {
this.index = index;
this.x = x;
this.y = y;
this.width = 100;
this.height = 100;
this.color = "rgba(0,0,0,0.5)";
this.draw();
}
draw() {
context.fillStyle = this.color;
context.fillRect(this.x, this.y, this.width, this.height);
context.fillStyle = "#fff";
context.fillText(this.index, this.x, this.y + 30);
}
}
let tempX, tempY;
for (let i = 0; i < 10; i++) {
// 박스가 캔버스 영역을 벗어나서 그려짐을 방지하기 위해 0.8을 곱해줌
tempX = Math.random() * canvas.width * 0.8;
tempY = Math.random() * canvas.height * 0.8;
boxes.push(new Box(i, tempX, tempY));
}
const clickHandler = (e) => {
mousePosition.x = e.offsetX;
mousePosition.y = e.offsetY;
let box;
let selectedBox; // 클릭된 박스를 넣어놓을 변수
for (let i = 0; i < boxes.length; i++) {
box = boxes[i];
if (
mousePosition.x >= box.x &&
mousePosition.x <= box.x + box.width &&
mousePosition.y >= box.y &&
mousePosition.y <= box.y + box.height
) {
selectedBox = box;
}
}
// 어떤 박스가 선택되었는지 확인
if (selectedBox) {
console.log(selectedBox.index);
}
};
canvas.addEventListener("click", clickHandler);
</script>
반복문 for문을 이용해서 랜덤 위치에 사각형을 그리고 별도의 배열에 저장을 한다.
canvas에 click 이벤트를 적용해 clickHandler함수를 실행하면 다시 반복문 for문을 이용해서 배열안에 있는 Box객체들을 돌면서 위치를 검증하고 선택유무를 확인한다.
사각형이 겹치는 위치에 있는 경우에는 가장 나중에 그려진 박스의 index를 출력한다.
<script>
const canvas = document.querySelector("canvas");
const context = canvas.getContext("2d");
const boxes = [];
const mousePosition = { x: 0, y: 0 };
context.font = "bold 30px sans-serif";
class Box {
constructor(index, x, y, speed, width, height) {
this.index = index;
this.x = x;
this.y = y;
this.speed = speed;
this.width = width;
this.height = height;
this.color = "rgba(0,0,0,0.5)";
this.draw();
}
draw() {
context.fillStyle = this.color;
context.fillRect(this.x, this.y, this.width, this.height);
context.fillStyle = "#fff";
context.fillText(this.index, this.x, this.y + 30);
}
}
const render = () => {
context.clearRect(0, 0, canvas.width, canvas.height);
let box;
for (let i = 0; i < boxes.length; i++) {
box = boxes[i];
box.x += box.speed;
if (box.x > canvas.width) {
box.x = -box.x;
}
box.draw();
}
requestAnimationFrame(render);
};
let tempX, tempY, tempSpeed, tempWidth, tempHeight;
for (let i = 0; i < 10; i++) {
// 박스가 캔버스 영역을 벗어나서 그려짐을 방하하기 위해 0.8을 곱해줌
tempX = Math.random() * canvas.width * 0.8;
tempY = Math.random() * canvas.height * 0.8;
// 1 ~ 2 사이의 값
tempSpeed = Math.random() * 2 + 1;
// 크기가 최소 30x30이고 100x100 이내의 랜덤한 상자크기를 만들고싶어
tempWidth = Math.random() * 70 + 30;
tempHeight = Math.random() * 70 + 30;
boxes.push(new Box(i, tempX, tempY, tempSpeed, tempWidth, tempHeight));
}
const clickHandler = (e) => {
mousePosition.x = e.offsetX;
mousePosition.y = e.offsetY;
let box;
let selectedBox; // 클릭된 박스를 넣어놓을 변수
for (let i = 0; i < boxes.length; i++) {
box = boxes[i];
if (
mousePosition.x >= box.x &&
mousePosition.x <= box.x + box.width &&
mousePosition.y >= box.y &&
mousePosition.y <= box.y + box.height
) {
selectedBox = box;
}
}
// 어떤 박스가 선택되었는지 확인
if (selectedBox) {
selectedBox.color = "red";
}
};
canvas.addEventListener("click", clickHandler);
render();
</script>
requestAnimationFrame을 이용해서 움직이는 사각형을 확인하고 click이 되었을때 색상을 변경하도록 적용하였다.
캔버스로 게임을 만들면 위의 내용들을 잘조합해서 만들수있겠다라는 생각이 문득 떠올랐다.