본문 바로가기
블로그 이미지

방문해 주셔서 감사합니다! 항상 행복하세요!

  
   - 문의사항은 메일 또는 댓글로 언제든 연락주세요.
   - "해줘","답 내놔" 같은 질문은 답변드리지 않습니다.
   - 메일주소 : lts06069@naver.com


Html 캔버스/Html 캔버스 튜토리얼 (내가만든 차트!)

Html Canvas (Html 캔버스) 튜토리얼 (차트만들기!) - 12 : 이벤트 관리3

야근없는 행복한 삶을 위해 ~
by 마샤와 곰 2020. 2. 11.

이벤트를 부여하는 방법 세번째 시간이다.

앞선 시간에는 사각형, 원에대해서 알아보았다면 여러 모양을 지닌 다각형에 대해서 이벤트를 주는 방법이다.

다각형은 말 그대로 여러 각을 지닌 도형을 의미한다.

아래 사진을 보자.

음..이걸 어떻게 하지..

 

위 사진처럼 특정 도형이 존재한다고 보자.

사각형에 이벤트를 부여하는 방법을 쓰기에는 빈 공간이 너무 많다.

마찬가지로 원에서 사용한 방법을 쓰기에도 중간이 아에 비어버렸으며, 마찬가지로 빈 공간이 많다.

즉, 기존에 원과 사각형에서 사용한 알고리즘을 적용시킬 수 없다라는 것 이다.

 

먼저 영역인 곳과 영역이 아닌곳을 클릭했다고 하여 보자.

빨강색 원은 영역이 아닌곳을 클릭한 경우, 검은색 원은 영역 내부를 클릭한 경우라고 하여보자.

아닌곳은 3군데 클릭, 맞는 곳은 2군데 클릭

 

사람은 한눈에 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의 캔버스(canvas)기반으로 동작하는 기능 입니다. Html 캔버스 튜토리얼 도 한번 살펴보세요~ :)
* 직접 만든 Html 캔버스 애니메이션 도 한번 살펴보세요~ :)

댓글