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

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

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


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

Html Canvas (Html 캔버스) 튜토리얼 (차트만들기!) - 15 : 바 차트 3

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

이번시간에는 차트에 이벤트를 부여하는 방법이다.

맨 처음에 설명하였듯이 캔버스는 1개의 객체로 이루어져 있기 때문에 이벤트를 부여하는 방법은 데이터를 통해서 직접 계산식으로 구현해야 된다고 했었다.

앞선시간에 이벤트 부여하는 방법 3가지를 해 보았었다.

 

1. 사각형에 이벤트 부여  https://lts0606.tistory.com/284

2. 원에 이벤트 부여  https://lts0606.tistory.com/285

3. 다각형에 이벤트 부여  https://lts0606.tistory.com/286

 

여기서는 Bar 차트이므로 1번 사각형에 이벤트를 부여하는 방법을 적용해 보도록 하겠다.

캔버스에서의 위치는 캔버스가 html로부터 띄어진 높이와, html 좌측에서부터 멀어진 거리를 빼 주어야 위치값이 나온다고 하였다.    * 기억이 안나면 사각형에 이벤트부여 방법을 다시보자.

 

그러면 지금껏 만들어진 코드에 매번 클릭이벤트를 주었으니 이번에는 마우스가 움직이는(mousemove)를 주어서 이벤트를 넣어보자.

기존 소스코드에 아래 소스코드를 추가하여보자.

canvas.addEventListener('mousemove', function (event) {
    var x1 = event.clientX - canvas.offsetLeft;  //캔버스가 html 좌측으로 떨어진 거리를 뺀다
    var y1 = event.clientY - canvas.offsetTop;   //캔버스가 html 위로부터 내려온 거리를 뺀다
    console.log(x1, y1);
}); 

 

요렇게 추가하면 캔버스 위로 마우스가 움직일 때 마다 캔버스의 x좌표와 y좌표값을 나타내어 준다.

그러면 그려진 대상에 대해서 마우스가 내부에 들어왔다는 어떻게 판별하는 것 일까?

지금 모습은 사각형이 일정하게 간격을 가지고 그려진 상태이다.

 

넓이에 들어왔음을 판별하려면,

마우스가 움직일 때 마다 사각형을 그리는 시작점+비율 ~ 시작점+넓이 이내에 마우스가 올라오면 해당 그래프가 선택된 것이라 볼 수 있다.

 

높이에 들어왔음을 판별하려면,

사각형이 그려지는 시작점(비율) ~ 최대높이 이내에 마우스가 올라오면 마찬가지로 해당 그래프가 선택된 것이라 볼 수 있다.

 

위 높이, 넓이에 대한 조건이 동시에 만족하면 그래프가 선택된 것 이다.

이를 계산식으로 풀어내어보면, 사각형 이벤트 부여하기 방법에서 본 계산방법과 거의 비슷한 함수가 만들어지게 된다.

function isInsideRect(x1, y1){
    var result = false;
    var index = -1;
    for(var i=0; i < value.length;i++){
        var data = value[i];

        var devide = i / value.length ;
        var start_x = position.min_x + position.max_x*devide; //x 시작점은 최소값 + 비율
        var end_x = start_x + wid;  //x 종료점은 시작값 + 넓이

        var start_y = position.max_y* ( 1-(data/100) );  //y시작점은 내부 사각형 그리기와 동일한 방법
        var end_y = position.max_y ; //y종료점도 내부 사각형 그리기와 동일한 방법
        
        if(x1 >= start_x && x1 <= end_x){  
            if(y1 >= start_y && y1 <= end_y){
                result = true;
                index = i;
                break;
            }
        } 
    }
    return {result:result, index:index};
}

 

해당 소스코드를 추가하여 실행하여보자.

사각형 영역에 들어올 때 마다 각각의 배열의 인덱스번호값을 가지고 결과가 나타나는 것을 볼 수 있다.

잘나온다!

 

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

<!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 position = {
        min_x : width * 0.1,
        max_x : width * 0.9,
        min_y : height * 0.1,
        max_y : height * 0.9
    }
    var wid = 50;

    var value = [90, 50, 60];
    var virtualVal = value.slice().map((arg)=>1); 
    var adder = 0;
    var interval = setInterval(() => {
        ctx.clearRect(0, 0, width, height);  //추가
        virtualVal.forEach( (data, idx) => {
            if(value[idx] > data){
                var devide = idx / value.length ;
                data++;
                virtualVal[idx] = data;                
                ctx.strokeRect(position.min_x + position.max_x*devide, position.max_y* ( 1-(data/100) ), wid, position.max_y - position.max_y* ( 1-(data/100) ));
            } else {  //추가
                var devide = idx / value.length ;
                ctx.strokeRect(position.min_x + position.max_x*devide, position.max_y* ( 1-(data/100) ), wid, position.max_y - position.max_y* ( 1-(data/100) ));                
            }
        });  
        var chekcer = virtualVal.slice().map((arg)=>false);
        virtualVal.forEach( (data1, idx1) => {
            value.forEach( (data2, idx2) => {
                if(data1 >= data2 && idx1 == idx2){
                    chekcer[idx1] = true;
                }
            });
        }); 

        var breaker = true;
        chekcer.forEach( arg => {
            if(!arg){
                breaker = false;
            }
        });     

        if(breaker){
            console.log('fin');
            clearInterval(interval);
            makeText()
        }
    }, 10);


    function makeText(){
        value.forEach((data, idx) =>{
            var devide = idx / value.length;
            var len = (wid/2) - ctx.measureText(data+'').width / 2;
            ctx.strokeText(data, position.min_x + position.max_x*devide + len, position.max_y + position.min_y/2);
        });        
    }
    


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

    function isInsideRect(x1, y1){
        var result = false;
        var index = -1;
        for(var i=0; i < value.length;i++){
            var data = value[i];

            var devide = i / value.length ;
            var start_x = position.min_x + position.max_x*devide; //x 시작점은 최소값 + 비율
            var end_x = start_x + wid;  //x 종료점은 시작값 + 넓이

            var start_y = position.max_y* ( 1-(data/100) );  //y시작점은 내부 사각형 그리기와 동일한 방법
            var end_y = position.max_y ; //y종료점도 내부 사각형 그리기와 동일한 방법
            
            if(x1 >= start_x && x1 <= end_x){  
                if(y1 >= start_y && y1 <= end_y){
                    result = true;
                    index = i;
                    break;
                }
            } 
        }
        return {result:result, index:index};
    }

    
</script>
    

 

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

다음시간에는 이벤트에 따른 색상변화에 대해서 살펴보도록 하겠다.

 

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

댓글