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

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

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


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

Html Canvas (Html 캔버스) 튜토리얼 (차트만들기!) - 19 : 원 차트 2

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

 

이번시간에는 원형 차트에 대한 이벤트에 대해서 살펴보도록 하겠다.

기존에 보았던 이벤트보다도 난이도가 조금 높다고 생각한다.

먼저 원형 이벤트에대해서 고려해야될 점이 2가지로 구분되어진다.

 

1. 원 반지름 내부에 들어왔는가?

2. 부채꼴 데이터가 이루고있는 각(degree) 범위에 존재하는가?

 

먼저 1번에 대한 해답은 예전시간에 피타고라스의 정리를 통해서 구하는 방법으로 해결하였었다.

 * 기억이 안나면 : https://lts0606.tistory.com/285

 

그러면 이제 2번에 대해서 해결방법을 찾아야한다.

아래사진을 보자.

1번 영역, 2번영역, 3번영역을 클릭하면?

 

사용자가 1번영역을 클릭하였다고하자.

피타고라스의 정리 계산을 통해서 우리는 원 내부에 마우스가 들어왔는지 여부를 판별 할 수 있다.

그런데..반지름까지는 판별했는데 1번인지 2번인지 아니면 3번인지를 모른다.

 

이를 해결하기위해서 먼저 선행되야 할 조건은 각각의 데이터가 차지하고있는 데이터의 각(degree)을 알고 있어야 한다는 점 이다.

앞시간에서 우리는 차트를 그릴 때 데이터를 시작과 종료각을 통해서 그려 주었다.

해당 각(degree)을 배열에 담도록 코드를 수정하여보자.

 

<!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 width = canvas.clientWidth;
    var height = canvas.clientHeight;

    var value = [100, 200, 300];
    var degree = 360;
    const radius = 50;

    var sum = value.reduce((a, b) => a + b);
    var conv_array = value.slice().map((data)=>{
        var rate = data / sum;
        var myDegree = degree * rate;
        return myDegree;
    });


    degree = 0;
    var event_array = value.slice().map( arg=> []);  //구간을 담는 배열, 이벤트시 활용한다.
    for(var i=0;i < conv_array.length;i++){
        var item = conv_array[i];
        ctx.save();
        ctx.beginPath();
        ctx.moveTo(width/2, height/2);
        if(i == 0){ 
            ctx.arc(width/2, height/2, radius, (Math.PI/180)*0, (Math.PI/180)* item , false);
            degree = item;
            event_array[i] = [0, degree];
        } else {
            ctx.arc(width/2, height/2, radius, (Math.PI/180)*degree, (Math.PI/180)*(degree + item), false);
            event_array[i] = [degree, degree+item];
            degree =  degree + item;
        }
        ctx.closePath();
        ctx.stroke();
        ctx.restore();
    }
    console.log(event_array);
</script>      

 

기존 소스코드에서 배열을 추가하여 시작각과 종료각을 담아주도록 하였다.

그러면 아직 방법은 모르지만 클릭이벤트가 발생하면 해당 배열을 통해서 누구의 데이터가 클릭 되었는지를 알 수 있게 되었다.

 

그런다음에 마찬가지로 이벤트를 부여하자.

이벤트를 부여할 때 고려할 점은 마찬가지로 캔버스가 html로부터 떨어진 거리, 좌측으로 멀어진 거리값을 빼 주어야 한다.

그리고나서 피타고라스의 정리를 통해서 반지름이 내부에 들어왔는지 까지만 적용하여보자.

아래스크립트를 붙여보자.

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);
}); 

function isInsideArc(x1, y1){ //이벤트가 누구인지를 판별하는 함수
    var result1 = false;
    var index = -1;
    var circle_len = radius;
    var x = width/2 - x1;
    var y = height/2 - y1;
    var my_len = Math.sqrt(Math.abs(x * x) + Math.abs(y * y));  //피타고라스의 정리
    if(circle_len >= my_len){  //중심점으로부터 반지름 이내에 들어왔는지 확인!
        result1 = true;
    }            
    return {result1:result1, index:index};
}

 

예전에 보았던 피타고라스의 정리를 통한 길이값과 반지름 길이값을 비교하는 기능이다.

여기서 추가해야될 기능은 이벤트로 클릭한 점의 각(degree)이다.

고등학교 수학시간을 떠 올려보면..탄젠트의 역함수인 아크탄젠트가 존재하는데..  * 수학입니다..ㅠ

아크탄젠트를 활용하면 점과 점사이의 각을 구할수가 있다.

탄젠트의 정의

 

고등학교 수학과정을 설명해야하므로..내용이 어렵다면 그냥 방향을 구하는건 "아크탄젠트를 사용한다" 로 이해하자.

그려면 해당 부분을 소스코드에 추가하여보자.

function isInsideArc(x1, y1){
    var result1 = false;
    var result2 = false;
    var index = -1;
    var circle_len = radius;
    var x = width/2 - x1;
    var y = height/2 - y1;
    var my_len = Math.sqrt(Math.abs(x * x) + Math.abs(y * y));  //피타고라스의 정리
    if(circle_len >= my_len){  //중심점으로부터 반지름 이내에 들어왔는지 확인!
        result1 = true;
    }            
    
    var rad = Math.atan2(y, x);  //아크탄젠트, 두 점사이의 라디안을 계산한다
    rad = (rad*180)/Math.PI;  //라디안을 사용하기 쉬운 각(degree)으로 형변환

    return {result1:result1, result2:result2 ,index:index, degree : rad};
}

 

자바스크립트에서 아크탄젠트는 Math.atan2라는 함수를 통해 사용 가능하다.

위에서 먼저 계산된 중심점과 이벤트를 받은 좌표값을 뺀 변수 x와 y를 Math.atan2 함수에 넣어주면 값이 나오게 된다.

위 내용을 실행하여보자.

result1은 내부에 들어왔는지여부, degree는 중심점 ~ 클릭한점이 이루는 각이다.

 

degree 값으로 데이터가 음수와 양수가 내용은 아직 어렵지만 잘 나오는 것을 볼 수 있다.

위 내용에 대한 최종 소스코드이다.

<!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 width = canvas.clientWidth;
    var height = canvas.clientHeight;

    var value = [100, 200, 300];
    var degree = 360;
    const radius = 50;

    var sum = value.reduce((a, b) => a + b);
    var conv_array = value.slice().map((data)=>{
        var rate = data / sum;
        var myDegree = degree * rate;
        return myDegree;
    });


    degree = 0;
    var event_array = value.slice().map( arg=> []);  //구간을 담는 배열, 이벤트시 활용한다.
    for(var i=0;i < conv_array.length;i++){
        var item = conv_array[i];
        ctx.save();
        ctx.beginPath();
        ctx.moveTo(width/2, height/2);
        if(i == 0){ 
            ctx.arc(width/2, height/2, radius, (Math.PI/180)*0, (Math.PI/180)* item , false);
            degree = item;
            event_array[i] = [0, degree];
        } else {
            ctx.arc(width/2, height/2, radius, (Math.PI/180)*degree, (Math.PI/180)*(degree + item), false);
            event_array[i] = [degree, degree+item];
            degree =  degree + item;
        }
        ctx.closePath();
        ctx.stroke();
        ctx.restore();
    }
    console.log(event_array);


    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);
    }); 


    function isInsideArc(x1, y1){
        var result1 = false;
        var result2 = false;
        var index = -1;
        var circle_len = radius;
        var x = width/2 - x1;
        var y = height/2 - y1;
        var my_len = Math.sqrt(Math.abs(x * x) + Math.abs(y * y));  //피타고라스의 정리
        if(circle_len >= my_len){  //중심점으로부터 반지름 이내에 들어왔는지 확인!
            result1 = true;
        }            
        
        var rad = Math.atan2(y, x);  //아크탄젠트, 두 점사이의 라디안을 계산한다
        rad = (rad*180)/Math.PI;  //라디안을 사용하기 쉬운 각(degree)으로 형변환

        return {result1:result1, result2:result2 ,index:index, degree : rad};
    }

</script>

 

여태껏 우리가 보아왔던 대부분의 Javascript로 이루어진 차트와 관련된 라이브러리들이..사실 내부를 뜯어보면 위 내용처럼 어렵고 복잡한 수학 계산식을 통해서 이루어져있다.

아크탄젠트에 대한 내용이 어려우면 검색을 통해서 해당 개념을 익혀도 좋다.

다음시간에는 만들어진 이벤트 결과를 응용하여 부채꼴이 영역안에 들어오면 크기가 변하는 효과를 만들어 보도록 하겠다.

 

 

 

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

댓글