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

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

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


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

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

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

 

저번시간에는 사각형 물체에 대해서 이벤트를 부여하는 방법에 대해서 살펴 보았다.

그러면 이제 원(구)에 대해서 살펴 볼 차레이다.

원에 대해서 이벤트를 부여하려면 사각형에서 사용하는 방법을 그대로 써도 되는데 그렇게하면 조금 어색한 문제가 발생 한다.

원을 클릭하는 건 상관없는데 저기 원 바깥영역은?

 

사각형에서 사용한 기법을 쓰게되면 위의 사진과 같은 문제가 발생한다.

원을 클릭하지 않고 주변을 클릭하였을 뿐인데...클릭이 되어버리는 현상이 나타난다.

그래서 원(구)에 대해서 이벤트를 부여 할 때는 반지름(radius)를 활용하여준다.

물론 타원인 경우에는 해당내용이 적용되지 않는다.

  * 타원은 타원의방정식을 활용하여야한다. 아래내용을 확인 후 나중에 찾아서 적용해보자. 여기서는 타원의 내용은 없다. ^-^;

 

자, 선행되어야할 조건은 2가지 이다.

1. 캔버스를 클릭했을 때 x, y 좌표값(캔버스가 Html에서부터 떨어진 거리값을 뺀 데이터)

2. 원을 그릴 때 중심점

 

1번은 앞 시간에 확인하였으므로 해당 기능을 그대로 사용하면된다.

2번은 캔버스에서 원을 그리는 기능은 arc 함수이며 첫번째와 두번째 파라미터로 x값과 y값을 넣어주면 해당 지점이 바로 중심점이 되기 때문에 따로 해줄 내용은 없다.

 

그렇다면 이제 이벤트를 부여하기 위한 개념을 살펴보자.

반지름, 클릭한 곳과의 거리비교!

 

캔버스를 클릭한 지점의 길이가 위 사진처럼 반지름보다 길면 원 외부에 존재하는 것을 알 수 있다.

그렇다면 클릭한 지점이 원의 반지름과 같거나 짧으면?

원의 반지름보다 같거나 짧으면 안에 들어가있는 경우만 해당되므로 원 내부를 클릭했다고 볼 수 있다.

조건 반지름 클릭한 지점
원 내부에 있는 경우 같거나 클릭한 지점보다 크다 같거나 반지름보다 작다
원 외부에 있는 경우 클릭한 지점보다 작다 반지름보다 크다

 

원의 반지름은 그림을 그릴 때 값을 주기 때문에 구하기 쉽다.

그러면 클릭한 지점으로부터 원의 중심점 까지의 거리는 어떻게 구하는 것 인가?

해당 내용은 중학교 교과과정에서 배우는 피타고라스의 정리를 사용하면 쉽게 가능하다.

요곱니다. 피타고라스의 정리!

https://ko.wikipedia.org/wiki/%EB%91%90_%EC%A0%90_%EC%82%AC%EC%9D%B4%EC%9D%98_%EA%B1%B0%EB%A6%AC

 

간만에 피타고라스의 정리를 만나서 당황하였겠지만..뭐..방법이 저게 가장 제일 쉬우니깐..

피타고라스의 정리를 활용해서 계산식을 만들어주면 내가 클릭한 지점 ~ 원 중심점 사이의 거리값이 나오게 된다.

이를 간단하게 자바스크립트를 사용하여 함수로 만들어보면,

function isInsideArc(x0, y0, x1, y1, radius){
    var result = false;
    var circle_len = radius;
    var x = x0 - x1;
    var y = y0 - y1;
    var my_len = Math.sqrt(Math.abs(x * x) + Math.abs(y * y));  //피타고라스의 정리
    if(circle_len >= my_len){
        result = true;
    } 
}

 

위 계산식이 나오게 된다.

피타고라스의 정리를 단순하게 자바스크립트로 만든 내용 이므로 그렇게 어렵지가 않다.

 

자, 이제 그러면 개념정리는 어느정도 되었으니 저번시간에 만든 기법까지 포함해서 만들어보자.

먼저 캔버스를 그린다.

마찬가지로 원이 n개가 될 수 있으므로 데이터는 배열에 넣어두고 원을 그린다.

<!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, radius:20},
        {x:85,y:100, radius:30},  
    ];

    function areaSimple(){
        position.forEach( (element,idx)=> {
            ctx.beginPath();
            ctx.arc(element.x, element.y, element.radius, 0* Math.PI, 2* Math.PI);
            ctx.fill();
        });
    }
    areaSimple()
</script>    

 

원 그리기에서 주의해야될 점은 beginPath를 통해서 한붓그리기를 종료해야 된다는 점 이다.

선 그리기와 비슷한 개념이라고 했었다.

아무튼 여기까지 하고나면 두개의 원이 그려진다.

 

위 소스코드에 클릭이벤트를 달아보자.

아래 스크립트코드를 붙여넣어보자.

canvas.addEventListener('click', function (event) {
    var x1 = event.clientX - canvas.offsetLeft;
    var y1 = event.clientY - canvas.offsetTop;
    console.log(x1, y1);
}); 

 

어렵지 않는 코드이다. 캔버스가 html에서 위에서 떨어진값, 좌측에서 멀어진값을 뺀 뒤에 캔버스 내부좌표값을 얻는 방법 이다.

그러면 이제 원 내부에 들어왔는지에 대한 피타고라스의 정리가 적용된 코드를 수정해볼 차레이다.

위에 있는 isInsideArc 함수는 지금은 배열에 대해서 적용되지 않았으므로 바꾸어보도록 하자.

function isInsideArc(x1, y1){
    var result = false;
    var index = -1;
    for(var i=0; i < position.length;i++){
        var data = position[i];
        var circle_len = data.radius;
        var x = data.x - x1;
        var y = data.y - y1;
        var my_len = Math.sqrt(Math.abs(x * x) + Math.abs(y * y));  //피타고라스의 정리
        if(circle_len >= my_len){
            result = true;
            index = i;  //배열 인덱스
            break;
        }            
    }
    return {result:result, index:index};  //결과
}

 

앞시간에서 보았던 사각형 내부의 판별 함수와 모습이 비슷하여졌다.

그러면 해당 함수를 이제 캔버스 클릭이벤트에 부여한 곳에 대입하여보자.

canvas.addEventListener('click', function (event) {
    var x1 = event.clientX - canvas.offsetLeft;
    var y1 = event.clientY - canvas.offsetTop;
    var inn = isInsideArc(x1, y1);
    console.log(inn);
}); 

 

요렇게 하고나서 한번 실행하여보면,

오옷..잘된다!!

 

결과값이 이상없이 잘 나옴을 볼 수 있다.

그러면 여기에 색깔변화 이벤트를 만들어보자.

사각형을 클릭했을 때 처럼 클릭하면 색을 바꾸고 일정 시간이 지나면 기존색으로 되돌려보자.

canvas.addEventListener('click', function (event) {
    var x1 = event.clientX - canvas.offsetLeft;
    var y1 = event.clientY - canvas.offsetTop;
    var inn = isInsideArc(x1, y1);
    console.log(inn);

    if (inn.result) {  //결과가 true이면
        ctx.clearRect(0, 0, 400, 400);  //초기화
        position.forEach( (element,idx)=> {
            ctx.save();  //기본 상태값 저장
            ctx.beginPath();
            if(inn.index == idx){  //만약 클릭된 대상이면
                ctx.fillStyle = 'blue';   
            }
            ctx.arc(element.x, element.y, element.radius, 0, 2* Math.PI);
            ctx.fill(); 
            ctx.restore();   //상태값 복원
        });
        setTimeout(() => {  //기존으로 되돌리기
            ctx.clearRect(0, 0, 400, 400);
            areaSimple();
        }, 100);
    }

}); 

 

위 코드를 실행하면 파랑색으로 색이 변하면서 기존색으로 되돌아가는 효과를 지닌 원을 만날 수 있게 된다.

이벤트 부여 완료!

 

위 내용에 대한 전체소스코드이다.

<!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, radius:20},
    {x:85,y:100, radius:30},  
];

function areaSimple(){
    position.forEach( (element,idx)=> {
        ctx.beginPath();
        ctx.arc(element.x, element.y, element.radius, 0* Math.PI, 2* Math.PI);
        ctx.fill();
    });
}

areaSimple();

canvas.addEventListener('click', function (event) {
    var x1 = event.clientX - canvas.offsetLeft;
    var y1 = event.clientY - canvas.offsetTop;
    var inn = isInsideArc(x1, y1);
    console.log(inn);

    if (inn.result) {
        ctx.clearRect(0, 0, 400, 400);
        position.forEach( (element,idx)=> {
            ctx.save();
            ctx.beginPath();
            if(inn.index == idx){
                ctx.fillStyle = 'blue';   
            }
            ctx.arc(element.x, element.y, element.radius, 0, 2* Math.PI);
            ctx.fill(); 
            ctx.restore();
        });
        setTimeout(() => {
            ctx.clearRect(0, 0, 400, 400);
            areaSimple();
        }, 100);
    }

}); 


function isInsideArc(x1, y1){
    var result = false;
    var index = -1;
    for(var i=0; i < position.length;i++){
        var data = position[i];
        var circle_len = data.radius;
        var x = data.x - x1;
        var y = data.y - y1;
        var my_len = Math.sqrt(Math.abs(x * x) + Math.abs(y * y));  //피타고라스의 정리
        if(circle_len >= my_len){
            result = true;
            index = i;
            break;
        }            
    }
    return {result:result, index:index};
}

   
</script>

 

이번시간에는 원에 대해서 이벤트를 부여하는 방법에 대해 알아보았다.

그러면 이제 남은 이벤트 대상은 1개!

 

바로 다각형 같은 물체이다.

만약 색연필 모양의 대상이 존재 한다면 해당 대상에 대해서는 이벤트를 부여하는 방법이 무엇일까?

기존의 사각형, 원 이벤트 부여방법과는 조금 다르다.

해당 방법에 대해서 살펴보고 난 뒤에 이제 기본적인 차트인 바(BAR) 차트를 한번 만들어보자.

 

반응형
* 위 에니메이션은 Html의 캔버스(canvas)기반으로 동작하는 기능 입니다. Html 캔버스 튜토리얼 도 한번 살펴보세요~ :)
* 직접 만든 Html 캔버스 애니메이션 도 한번 살펴보세요~ :)

댓글