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

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

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


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

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

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

 

앞전시간에는 도형을 그리고, 애니메이션을 주는 부분까지하여 단순한 차트의 형태를 구성하여 보았다.

이번에는 차트를 깔끔하게 그리는 방법과, 입력된 데이터가 하단에 표시되는 방법에 대해서 알아보겠다.

 

캔버스 내부에 차트를 그릴때 strokeRect함수를 사용해서 계속해서 그렸다.

그러다보니 내부에 줄이 그어진 상태로 차트가 완성되어서 되게 보기 불편한 모양이 되었다.

 

내부에 선이 많다. 사각형이 계속해서 중첩되서 그려지니까..

 

이러한 현상을 좀 해결하려면 가장 간단한 방법인 "지우고, 다시그리고" 방법을 써 주면 된다.

애니메이션을 주는 방법이 "지우고, 다시그리고" 방법이 짧은 시간동안 지속되면 마치 움직이는 효과를 나타내는 것 이라고 했으므로 처음 그리는 방법에서의 코드를 간단하게 수정하면 깔끔한 모양의 차트가 나오게 된다.

앞 시간 코드의 setInterval 부분을 수정해보도록 하자.

 

var interval = setInterval(() => {
    ctx.clearRect(0, 0, width, height);  //추가1
    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 {  //추가2
            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);
    }
}, 10);

 

"추가1" 에서의 캔버스는 지우는 효과를 실행하여 초기화를 하여준다.

"추가2" 에서 값이 변하지 않으면 기존 효과 그대로를 그리게 하였다.

수정된 코드를 실행하면 내부의 중첩된 선이 그려지는 것이 깔끔하게 해결된다.

 

내부가 깔끔하게 그려졌다.

 

지우고, 그리기를 반복하니 훨씬 깔금한 형태의 모양이 되었다.

물론 여기서는 stroke을 통해서 채우기 보다는 선긋기 모양을 표현하고 있으나 fill 형태로 채우기를 사용해도 상관 없다.

 

 

자, 다음으로 사용자가 입력한 데이터를 그려진 차트 아래에 표현하여보자.

기억을 더듬어 보면 텍스트를 그릴 때 사용하는 함수는 fillText 또는 strokeText를 통해서 실시 하였다.

마찬가지로 텍스트를 넣을 때도 그리는 방법을 이어서 하여주면된다.

그런데..텍스트를 굳이 여러번 그릴 필요가 없으니 애니메이션이 끝나는 부분에 넣어주도록 하자.

먼저 텍스트를 그릴 함수를 만든다.

function makeText(){
    value.forEach((data, idx) =>{
        var devide = idx / value.length ;
        ctx.strokeText(data, position.min_x + position.max_x*devide, 100);
    });
}    

 

x좌표는 앞시간에 한 방법처럼 비율을 포함해서 간격을 주는 방법을 선택하였다.

해당 함수를 console.log('fin'); 이 존재하는 구간에 넣어주자.

이렇게 하고나면 텍스트가 높이는 100인 상태로 나타나게된다.

그려지긴 했는데...높이도 그렇고 간격도 좀...

 

간격이 좀 애매하다.. 뭔가 중앙에 온 것 같지도 않고.

이를 수정하려면 앞시간에 살펴보았던 캔버스에서 제공한다는 텍스트의 길이를 그려주는 measureText 함수를 사용하면 쉽게 가능하다.

텍스트의 길이값 절반만큼 이동을 더 하도록 수정하여보자.

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

 

요걸 실행하면 아래 모습을 볼 수 있다.

 

텍스트가 중앙에 나나탔다.

 

텍스트를 저대로 두어도 상관 없지만 y좌표를 최댓값에서 최소값의 절반을 더해주면 텍스트가 하단부에 위치하게된다.


function makeText(){
    value.forEach((data, idx) =>{
        var devide = idx / value.length;
        var len = (wid/2) - ctx.measureText(data+'').width / 2;
        //최대높이 + 최소높이/2 만큼 텍스트를 아래로 가게 하였다.
        ctx.strokeText(data, position.min_x + position.max_x*devide + len, position.max_y + position.min_y/2);
    });        
}

 

이렇게 하고나면 이제 좀 모양이 제법 그럴싸해진 차트가 완성이 된다.

음 뭔가 차트같아보이는군..

 

최종 소스코드이다.

<!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 interval = setInterval(() => {
        ctx.clearRect(0, 0, width, height);  //추가1
        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 {  //추가2
                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);
        });        
    }
    
</script>

 

이번시간에는 차트를 좀 더 깔끔하게 수정하고 텍스트를 표현하여보았다.

다음시간에는 차트에 이벤트를 부여해보도록 하겠다.

 

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

댓글