해당 내용은 판교고 사이드 프로젝트 진행시에 사용할 캔버스에 대한 학습 내용을 정리하였습니다.
동일한 타일 여부 확인
이전 게시물에서 타일을 마우스를 통해 드래그하는 기능까지 완료하였습니다. 이번에는 타일을 목표 타일에 인접하게 이동시켰을 때 동일한 타일인 경우 자동으로 목표 타일로 이동되는 기능을 추가해 보겠습니다.
타일을 이동시킬때마다 아래의 두가지를 확인합니다.
- 드래그 중인 타일이 타겟에 인접했는가
- 동일한 타일인가
조건 두가지를 모두 만족하는 경우 타일을 타겟의 위치로 이동시켜주면 완성됩니다. 두가지 조건을 타일을 이동시키는 코드에 추가하도록 하겠습니다.
먼저 두개의 타일이 동일한지 여부를 확인하는 isEqual 함수를 만들어보겠습니다. 타일은 사각형이기 때문에 가로와 세로의 길이가 동일하다면 같은 타일입니다.
const isEqual = (target, source) => (
target.width === source.width && target.height === source.height
);
두번째로 타일이 인접한지 여부를 확인해보겠습니다. 목표와 타일의 간격이 GAP 보다 작은 경우 인접한 것으로 판단합니다.
const isClose = (target, source) => {
const GAP = 20;
if (source.x + GAP < target.x) {
return false;
}
if (source.y + GAP < target.y) {
return false;
}
if (target.x + target.width + GAP < source.x + source.width) {
return false;
}
if (target.y + target.height + GAP < source.y + source.height) {
return false;
}
return true;
}
두개의 함수를 적용하기 위해서 moveRectangle 함수를 수정하겠습니다. 이동 중 목표와 인접한지, 동일한지 여부를 확인하는 코드를 추가하겠습니다.
const moveRectangle = (target, downEvent) => {
const [currentX, currentY] = [target.x, target. y];
return (moveEvent) => {
const [movementX, movementY] = [downEvent.x - moveEvent.x, downEvent.y - moveEvent.y];
target.x = currentX - movementX;
target.y = currentY - movementY;
if (isEqual(targetRectangle, target) && isClose(targetRectangle, target)) {
target.x = targetRectangle.x;
target.y = targetRectangle.y;
}
update = true;
}
};
여기까지 판교고 사이드 프로젝트에서 활용할 캔버스를 알아보는 시간을 가졌습니다. 아직까지는 캔버스를 다루는데 미숙하지만 캔버스가 어떤식으로 작동하는지에 대해 어렴풋하게 알 수 있었습니다.
아래는 지금까지 작성한 전체 코드입니다.
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const rectangleList = [];
let targetRectangle = null;
let update = true;
const init = () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
const createRectangle = () => {
for (let i = 0; i < 5; i += 1) {
const x = Math.floor(Math.random() * 20) * 20;
const y = Math.floor(Math.random() * 20) * 20;
const width = Math.floor(Math.random() * 20) * 20 + 50;
const height = Math.floor(Math.random() * 20) * 20 + 50;
rectangleList.push({ x, y, width, height });
}
};
const createTargetRectangle = () => {
const x = 800;
const y = 300;
const width = Math.floor(Math.random() * 20) * 20 + 50;
const height = Math.floor(Math.random() * 20) * 20 + 50;
targetRectangle = { x, y, width, height };
// 목표 도형과 동일한 사이즈의 도형을 만들기 위한 코드
rectangleList.push({
x: Math.floor(Math.random() * 20) * 20,
y: Math.floor(Math.random() * 20) * 20,
width,
height
});
};
const draw = () => {
requestAnimationFrame(draw);
if (!update) return;
ctx.clearRect(0,0, window.innerWidth, window.innerHeight);
ctx.strokeStyle = 'black';
rectangleList.forEach(({ x, y, width, height }) => {
ctx.strokeRect(x, y, width, height);
});
ctx.strokeStyle = 'red';
ctx.strokeRect(
targetRectangle.x,
targetRectangle.y,
targetRectangle.width,
targetRectangle.height,
);
update = false;
};
const isEqual = (target, source) => (
target.width === source.width && target.height === source.height
);
const isClose = (target, source) => {
const GAP = 20;
if (source.x + GAP < target.x) {
return false;
}
if (source.y + GAP < target.y) {
return false;
}
if (target.x + target.width + GAP < source.x + source.width) {
return false;
}
if (target.y + target.height + GAP < source.y + source.height) {
return false;
}
return true;
}
const moveRectangle = (target, downEvent) => {
const [currentX, currentY] = [target.x, target. y];
return (moveEvent) => {
const [movementX, movementY] = [downEvent.x - moveEvent.x, downEvent.y - moveEvent.y];
target.x = currentX - movementX;
target.y = currentY - movementY;
if (isEqual(targetRectangle, target) && isClose(targetRectangle, target)) {
target.x = targetRectangle.x;
target.y = targetRectangle.y;
}
update = true;
}
};
const contains = (rectangle, { x, y }) => {
if (rectangle.x > x || rectangle.y > y) {
return false;
}
if ((rectangle.x + rectangle.width) < x || (rectangle.y + rectangle.height) < y) {
return false;
}
return true;
}
canvas.addEventListener('mousedown', (downEvent) => {
const { pageX, pageY } = downEvent;
const target = rectangleList.find((rectangle) => (
contains(rectangle, { x: pageX, y: pageY })
));
if (!target) return;
const moveHandler = moveRectangle(target, downEvent);
canvas.addEventListener('mousemove', moveHandler);
canvas.addEventListener('mouseup', () => {
canvas.removeEventListener('mousemove', moveHandler);
}, { once: true });
});
document.addEventListener('DOMContentLoaded', () => {
init();
createRectangle();
createTargetRectangle();
draw();
})
'사이드 프로젝트 > 판교고' 카테고리의 다른 글
[판교고 - 기술체크] 1-2. 캔버스에 타일 드래그로 이동 (0) | 2023.04.25 |
---|---|
[판교고 - 기술체크] 1-1. 캔버스에 타일 배치 (0) | 2023.04.21 |