이벤트를 부여하는 방법 세번째 시간이다.
앞선 시간에는 사각형, 원에대해서 알아보았다면 여러 모양을 지닌 다각형에 대해서 이벤트를 주는 방법이다.
다각형은 말 그대로 여러 각을 지닌 도형을 의미한다.
아래 사진을 보자.
위 사진처럼 특정 도형이 존재한다고 보자.
사각형에 이벤트를 부여하는 방법을 쓰기에는 빈 공간이 너무 많다.
마찬가지로 원에서 사용한 방법을 쓰기에도 중간이 아에 비어버렸으며, 마찬가지로 빈 공간이 많다.
즉, 기존에 원과 사각형에서 사용한 알고리즘을 적용시킬 수 없다라는 것 이다.
먼저 영역인 곳과 영역이 아닌곳을 클릭했다고 하여 보자.
빨강색 원은 영역이 아닌곳을 클릭한 경우, 검은색 원은 영역 내부를 클릭한 경우라고 하여보자.
사람은 한눈에 3군데는 아닌곳을 선택했고, 2군데가 도형을 선택했다고 알 수 있다.
그런데 이것을 컴퓨터가 알기에는 특정 패턴이 필요하다.
여기서 각 원을 중심으로 우측에 수평선을 그어보자.
자세히 살펴보면, 다각형 외부에 있는 접점은 짝수 또는 0 이다.
다각형 내부에 있는 점의 접점은 홀수이다.
wow...이러한 규칙이!!
다각형 내부에 있는, 다각형 내부에 들어온 이벤트는 홀수인 경우에 참이 되는 것 이다.
이를 계산식으로 나타내면,
function isInside(event) {
//crosses는 점과 오른쪽 반직선과 다각형과의 교점의 개수
var cross = 0;
for (var i = 0; i < position.length; i++) {
var pos = position[i];
var j = (i + 1) % position.length; //교점구할 j값, 1,2,3..순으로 인덱스가 증가하다 마지막은 초기값 0이 나와 비교를 함.
var otherPos = position[j];
if ((pos.y > event.y) != (otherPos.y > event.y)) { //점 event가 선분에 있는지 여부
//visit는 점 event를 지나는 수평선과 선분(pos, otherPos)의 교점
var visit = (otherPos.x - pos.x) * (event.y - pos.y) / (otherPos.y - pos.y) + pos.x;
//atX가 오른쪽 반직선과의 교점이 맞으면 교점의 개수를 증가시킨다.
if (event.x < visit){
cross++;
}
}
}
return cross % 2 > 0; //홀짝 후 리턴
}
요렇게 풀어낼 수 있다.
음..위 함수를 이해하는 것이 쉽지 않으므로..이해가 되지 않는다면 그냥 이렇게 쓴다고 넘어가도록 하자..ㅠ
그러면 기본 도형을 그려보자.
position이라는 객체에는 다각형을 그리기위한 좌표값이 들어가 있다.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Canvas</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<style>
body{padding: 5%;text-align: center;}
canvas{border: 1px solid gray;border-radius: 3px;}
</style>
<body>
<canvas width='400' height='400' id='canvas'></canvas>
</body>
</html>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
var position = [
{x:50,y:50},
{x:85,y:100},
{x:100,y:150},
{x:70,y:150},
{x:30,y:70},
{x:20,y:30},
{x:50,y:50}
];
function areaSimple(){
ctx.beginPath();
position.forEach( (element,idx)=> {
if(idx == 0){
ctx.moveTo(element.x,element.y)
} else {
ctx.lineTo(element.x,element.y)
}
});
ctx.stroke();
}
areaSimple();
</script>
선 그리기를 통해서 다각형을 그려보았다.
모양은 원하는데로 아무렇게나 해도 상관없다.
앞선 시간에서 매번 나왔던 클릭 이벤트를 부여해보자.
그리고 맨 처음 설명한 함수와 결합하여보자.
스크립트코드를 수정하여본다.
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
var position = [
{x:50,y:50},
{x:85,y:100},
{x:100,y:150},
{x:70,y:150},
{x:30,y:70},
{x:20,y:30},
{x:50,y:50}
];
function areaSimple(){
ctx.beginPath();
position.forEach( (element,idx)=> {
if(idx == 0){
ctx.moveTo(element.x,element.y)
} else {
ctx.lineTo(element.x,element.y)
}
});
ctx.stroke();
}
areaSimple();
canvas.addEventListener('click', function (event) {
var x1 = event.clientX - canvas.offsetLeft;
var y1 = event.clientY - canvas.offsetTop;
var result = isInside({x:x1, y:y1});
console.log(x1, y1, result)
});
function isInside(event) {
//crosses는 점과 오른쪽 반직선과 다각형과의 교점의 개수
var cross = 0;
for (var i = 0; i < position.length; i++) {
var pos = position[i];
var j = (i + 1) % position.length; //교점구할 j값, 1,2,3..순으로 인덱스가 증가하다 마지막은 초기값 0이 나와 비교를 함.
var otherPos = position[j];
if ((pos.y > event.y) != (otherPos.y > event.y)) { //점 event가 선분에 있는지 여부
//visit는 점 event를 지나는 수평선과 선분(pos, otherPos)의 교점
var visit = (otherPos.x - pos.x) * (event.y - pos.y) / (otherPos.y - pos.y) + pos.x;
//atX가 오른쪽 반직선과의 교점이 맞으면 교점의 개수를 증가시킨다.
if (event.x < visit){
cross++;
}
}
}
return cross % 2 > 0; //홀짝 후 리턴
}
그러면 요 코드를 실행하면..두근두근~
매우 훌륭한 모습이다.. isInside 함수를 통해서 계산된 접점에 대한 선분 구하는 함수가 매우 잘 작동하였다.
그러면 기존해 했던데로 색깔이 변하는 이벤트를 만들어보자.
최종 완성된 소스코드이다.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Canvas</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<style>
body{padding: 5%;text-align: center;}
canvas{border: 1px solid gray;border-radius: 3px;}
</style>
<body>
<canvas width='400' height='400' id='canvas'></canvas>
</body>
</html>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
var position = [
{x:50,y:50},
{x:85,y:100},
{x:100,y:150},
{x:70,y:150},
{x:30,y:70},
{x:20,y:30},
{x:50,y:50}
];
function areaSimple(){
ctx.beginPath();
position.forEach( (element,idx)=> {
if(idx == 0){
ctx.moveTo(element.x,element.y)
} else {
ctx.lineTo(element.x,element.y)
}
});
ctx.stroke();
}
areaSimple();
canvas.addEventListener('click', function (event) {
var x1 = event.clientX - canvas.offsetLeft;
var y1 = event.clientY - canvas.offsetTop;
var result = isInside({x:x1, y:y1});
console.log(x1, y1, result)
if(result){
ctx.save();
ctx.clearRect(0,0,400,400);
areaSimple();
ctx.fillStyle= 'blue';
ctx.fill();
ctx.restore();
setTimeout(() => {
ctx.clearRect(0,0,400,400);
areaSimple();
}, 100);
}
});
function isInside(event) {
//crosses는 점과 오른쪽 반직선과 다각형과의 교점의 개수
var cross = 0;
for (var i = 0; i < position.length; i++) {
var pos = position[i];
var j = (i + 1) % position.length; //교점구할 j값, 1,2,3..순으로 인덱스가 증가하다 마지막은 초기값 0이 나와 비교를 함.
var otherPos = position[j];
if ((pos.y > event.y) != (otherPos.y > event.y)) { //점 event가 선분에 있는지 여부
//visit는 점 event를 지나는 수평선과 선분(pos, otherPos)의 교점
var visit = (otherPos.x - pos.x) * (event.y - pos.y) / (otherPos.y - pos.y) + pos.x;
//atX가 오른쪽 반직선과의 교점이 맞으면 교점의 개수를 증가시킨다.
if (event.x < visit){
cross++;
}
}
}
return cross % 2 > 0; //홀짝 후 리턴
}
</script>
위 코드를 실행하면 멋지게 색이 변하는 모습을 볼 수 있다.
사각형, 원, 다각형 등을 통해서 이벤트를 부여하는 방법에 대해서 살펴 보았다.
캔버스는 단일객체로 이루어져 있으므로 이벤트나 효과를 주기위해서는 그리기 / 지우기가 반복이 되어야 한다.
각종 계산식을 통해서 이러한 내용이 구현되어야 하는 것이 바로 Html Canvas이다.
다음시간에는 간단한 Bar 모양의 그래프를 만들어보자.
'Html 캔버스 > Html 캔버스 튜토리얼 (내가만든 차트!)' 카테고리의 다른 글
Html Canvas (Html 캔버스) 튜토리얼 (차트만들기!) - 14 : 바 차트 2 (0) | 2020.02.13 |
---|---|
Html Canvas (Html 캔버스) 튜토리얼 (차트만들기!) - 13 : 바 차트 1 (0) | 2020.02.12 |
Html Canvas (Html 캔버스) 튜토리얼 (차트만들기!) - 11 : 이벤트 관리2 (0) | 2020.02.10 |
Html Canvas (Html 캔버스) 튜토리얼 (차트만들기!) - 10 : 이벤트 관리1 (0) | 2020.02.07 |
Html Canvas (Html 캔버스) 튜토리얼 (차트만들기!) - 9 : 원 그리기 (0) | 2020.02.07 |
댓글