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

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

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


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

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

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

 

저번시간까지 이벤트를 부여하는 방법에 대해서 살펴 보았다.

이번시간에는 부채꼴 내부에 텍스트를 넣어보도록 하겠다.

 

앞전 시간에 알게된 사실은 아래 내용이다.

1. 원의 반지름

2. 원의 중심점

3. 점과 점사이의 각(degree) 구하는 방법

4. 이벤트 영역의 시작각(degree)  ~ 종료각(degree) 범위 배열

 

여기에 수학적 방법(?)을 동원하면 텍스트가 들어갈 위치를 만들어낼 수 있다.

아래 고등학교(?) 시절 배웠던 사인과 코사인을 보도록 하자.

맨날 수학적 계산이군요...

 

 

위 내용을 토대로 텍스트가 들어갈 영역을 만들어 낼 수 있다.

이벤트가 담긴 event_array 영역에서의 시작각 ~ 종료각 차이를 구한뒤에 절반으로 나누게 되면 텍스트가 해당 영역의 가운데 들어가게 될 것 이다.

말이 어려우므로 아래 코드를 살펴보자.

//도(degree)를 라디안(radian)으로 바꾸는 함수
function degreesToRadians(degrees) {
    const pi = Math.PI;
    return degrees * (pi / 180);
}

event_array.forEach(itm => {
    var half = (itm[1] - itm[0]) / 2;
    var degg = itm[0] + half;
    var xx = Math.cos(degreesToRadians(degg)) * radius + width/2;
    var yy = Math.sin(degreesToRadians(degg)) * radius + height/2;
    console.log(xx,yy);
    ctx.rect(xx,yy,20,20); 
    ctx.stroke();
});

 

event_array를 반복문으로 동작시킨뒤에 시작각 ~ 종료각 차이를 구한뒤에 절반으로 나누었다.

Javascript에서 사인과 코사인은 라디안(radian) 값을 사용하므로 degreeToRadians라는 함수를 만든뒤에 변환하였다.

radius는 반지름이고, width과 height은 중심점을 의미한다.

그리고 해당 각을 시작각에 더해서 부채꼴이 바라보는 중앙에 오도록 좌표를 만든 뒤에 사각형을 그렸다.

 

해당 소스코드를 붙여서 실행하면,

요렇게 사각형이 부채꼴에 중앙에 위치한다.

 

 

위 사진과 같은 내용으로 나오게 된다.

그렇다면 저 사각형을 대신하여 텍스트를 그리게 바꾸면 되는 것 이다.

만약 텍스트를 안쪽으로 오게 하려면 반지름 값을 줄이면되고, 멀리가게 하려고 하면 반지름 값을 크게하면된다.

요런 개념 입니다. ^^

 

 

전체 소스코드이다.

<!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, 100, 100];
    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();
    }

    var drawed = false;
    canvas.addEventListener('mousemove', function (event) {
        var x1 = event.clientX - canvas.offsetLeft;
        var y1 = event.clientY - canvas.offsetTop;
        var inn = isInsideArc(x1, y1);
        //console.log(inn);
        if(inn.index > -1){
            drawed = true;
            hoverCanvas(inn.index);
        } else {
            if(drawed){
                hoverCanvas(-1);
            }
            drawed = false;
        }
    }); 

    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;  //음수가 나온다
        rad += 180;  //캔버스의 각도로 변경

        if(result1){
            event_array.forEach( (arr,idx) => {   //각도 범위에 해당하는지 확인
                if( rad >= arr[0] && rad <= arr[1]){
                    result2 = true;
                    index = idx;
                }
            });
        }
        return {result1:result1, result2:result2 ,index:index, degree : rad};
    }


    function hoverCanvas(index){
        ctx.clearRect(0,0,width, height);
        for (var i = 0; i < conv_array.length; i++) {
            var item = conv_array[i];
            ctx.save();
            ctx.beginPath();
            var innRadius = radius;
            ctx.moveTo(width / 2 , height / 2 );
            if(index == i){  
                ctx.lineWidth = 2;
                ctx.strokeStyle='blue';
                innRadius = radius * 1.1;
            } 
            if (i == 0) {
                ctx.arc(width / 2, height / 2, innRadius, (Math.PI / 180) * 0, (Math.PI / 180) * item, false);
                degree = item;
            } else {
                ctx.arc(width / 2, height / 2, innRadius, (Math.PI / 180) * degree, (Math.PI / 180) * (degree + item), false);
                degree = degree + item;
            }
            ctx.closePath();
            ctx.stroke();
            ctx.restore();
        }
    }


    //도(degree)를 라디안(radian)으로 바꾸는 함수
    function degreesToRadians(degrees) {
        const pi = Math.PI;
        return degrees * (pi / 180);
    }


    event_array.forEach(itm => {
        var half = (itm[1] - itm[0]) / 2;
        var degg = itm[0] + half;
        var xx = Math.cos(degreesToRadians(degg)) * radius + width/2;
        var yy = Math.sin(degreesToRadians(degg)) * radius + height/2;
        console.log(xx,yy);
        ctx.rect(xx,yy,20,20); 
        ctx.stroke();
    });
</script>
    

 

아직 불완전 하지만 텍스트가 들어갈 위치는 확보한 것 같다.

다음시간에는 실제로 텍스트를 넣어서 차트를 좀 더 세련되게 완성해보도록 하자.

 

 

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

댓글