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

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

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


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

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

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

이번시간에는 부여한 이벤트에 색상을 바꾸는 기능을 추가해 보겠다.

mosemove에서 해당 영역안에 마우스가 올라오면 색이 변해야되고, 마우스가 out되면 색이 원래대로 되돌어 가야한다.

그렇다면 이러한 개념을 글로 풀어보면,

 

1. 마우스가 차트의 bar 내부에 있으면 색이 변해야한다.

2. 마우스가 차트bar의 외부로 나가면 색이 기존으로 돌아와야한다.

 

2가지 개념으로 정리가 된다.

다시그리기 개념을 적용하면 위 2가지 조건을 만족시킬 수 있다.

이를 함수로 표현하여보자.

function hoverDrawing(hoverIdx){
    ctx.clearRect(0,0,width, height);  //초기화
    value.forEach( (data, idx) => {
        ctx.save();  //정보저장
        if(idx == hoverIdx){   //대상인 경우
            ctx.strokeStyle = 'blue';
        }
        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) ));
        ctx.restore();
    });      
    makeText();  //텍스트 그리기
}

 

초기화를 하기위해서 clearRect가 맨 처음 동작한다.

그리고나서 값이 있는 value 배열을 반복문을 돌려서 차트를 다시그리게 하였다.

그러다가 조건문으로 대상의 아이디 값이 들어오면 색을 파랑색을 칠하게 하였다.

이렇게 하다보면 초기화를 통해서 텍스트가 지워졌으므로 makeText함수를 다시 호출하여 텍스트를 그리게 하였다.

 

이제 해당 함수를 이벤트를 준 mousemove에 포함시켜보자.

 
function hoverDrawing(hoverIdx){
    ctx.clearRect(0,0,width, height);  //초기화
    value.forEach( (data, idx) => {
        ctx.save();  //정보저장
        if(idx == hoverIdx){   //대상인 경우
            ctx.strokeStyle = 'blue';
        }
        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) ));
        ctx.restore();  //정보복구
    });      
    makeText();  //텍스트 그리기
}

var drawed = false;  //그려졌는지 여부 확인
canvas.addEventListener('mousemove', function (event) {
    var x1 = event.clientX - canvas.offsetLeft;
    var y1 = event.clientY - canvas.offsetTop;        
    var inn = isInsideRect(x1, y1);
    //console.log(inn);
    if (inn.result) {
        drawed = true;
        hoverDrawing(inn.index);  //오버효과
    } else {
        if(drawed) hoverDrawing(-1);  //그려진 경우에만 원래모양으로 되돌리기
    }
}); 

 

그리고나서 위 코드를 실행하면,

마우스가 올라가면 파랑색으로 변한다.

 

정상적으로 잘 표현됨을 알 수 있다.

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

<!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];
    function drawing(){
        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(virtualVal);
                console.log('fin');
                clearInterval(interval);
                makeText()
            }
        }, 10);        
    }

    drawing();

    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);
        });        
    }
    
    function hoverDrawing(hoverIdx){
        ctx.clearRect(0,0,width, height);  //초기화
        value.forEach( (data, idx) => {
            ctx.save();  //정보저장
            if(idx == hoverIdx){   //대상인 경우
                ctx.strokeStyle = 'blue';
            }
            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) ));
            ctx.restore();
        });      
        makeText();  //텍스트 그리기
    }

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

    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;
            var end_x = start_x + wid;
            var start_y = position.max_y* ( 1-(data/100) );
            var end_y = position.max_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>
    

 

여기서 나중에 생각해볼점은, 지속해서 그리는 행위를 어떻게하면 조금만, 최소화화여 실행시킬 것에 대한 문제이다.

지금은 데이터가 3개뿐이지만 나중에 100개, 1000개 등등 그려야할 차트의 데이터가 많아지면 많아질 수록 차트를 계속해서 그리는 행위가 브라우저에 무리가 올 수 있기 때문이다.

정말 나~~중에 생각하여보자!

 

아무튼!

여기까지해서 거의 차트에서 볼 수 있는 기능은 다해본 것 같다.

다음시간에는 Bar차트 마지막으로 툴팁과같은 효과를 나타내어 보도록 하겠다.

 

 

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

댓글