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

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

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


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

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

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

 

드디어 대망의 캔버스를 통한 차트만들기시간 첫번째이다.

차트는 기본적인 BAR 형태의 차트를 만들어보도록 하겠다.

앞선 내용들을 어느정도 숙지하고나서 아래 내용을 살펴보도록 하자.

 

사용자가 차트를 그리고싶어하는 데이터는 x좌표, y좌표와 높이 넓이 등이 포함되어있는 데이터가 아니다.

오직 순수하게 값(value)만 존재하는 데이터를 가지고 차트가 나타나주기를 바란다.

그러면 차트를 구성하려면 무엇부터 하여야 할까?

 

먼저 좌표의 개념부터 정리하여보자.

사용자가 데이터를 입력하였다고하면 캔버스의 특성상 값이 크면 맨 위의 지점은 조금만 떨어저야하고, 값이 작으면 많이 내려가야한다.

사진을 살펴보자.

요렇게~

 

캔버스의 y좌표는 아래로 향할수록 값이 커진다.

그래프로 표현해야될 값이 커질수록 내가 그리려는 차트는 맨위 상단에서 조금만 떨어져야하며,

값이 작으면 작을수록 맨 위 상단에서 많이 멀어져야하는 구조이다.

 

이를 공식화 하여보면 아래 사진처럼 나타낼 수 있다.

또 계산이군..

 

뭐..이렇게만 놓고보면 이해가 안되니 코딩을 하여보자.

아래소스코드를 입력하자.

<!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,
        max_y : height * 0.9,
        min_y : height * 0.1
    }

    var value = 90;  //사용자가 입력한 값
    var wid = 50;  //바 차트 1개의 넓이
</script>      

 

position이라는 객체는 캔버스의 최소, 최대값을 지닌 정보를 담은 객체이다.

왜 이러한 개념이 도입되었냐하면 여백효과를 주기 위해서이다.

만약 캔버스에 도형을 그릴 때 여백효과를 주지않으면 아래 사진모양처럼 나오게 될 것 이다.

 

그런다음에 맨 처음 설명한 방법데로 사각형을 그려보자.

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,
    max_y : height * 0.9,
    min_y : height * 0.1
}

var value = 90;
var wid = 50;
ctx.save();
//시작, 시작, 넓이, 높이

//시작점 x는 position에서 옆으로 띄울 최소값
//시작점 y는 최대높이값에서 비율을 곱해서 나온 값
//사각형의 넓이는 wid 값으로 고정
//사각형의 높이는 최대 높이에서 비율을 곱해나온 값을 빼 준 값
ctx.strokeRect(
  position.min_x, 
  position.max_y* ( 1-(value/100) ), 
  wid, 
  position.max_y - position.max_y* ( 1-(value/100) )
);

 

요걸 실행하면..

바 차트가 그려졌습니다~

 

value값을 0~100사이 값을 입력하여보자.

맨 위 상단이 값이 크면 클 수록 조금만 떨어지게되어 나름 바 차트의 기본골격을 갖춘 것을 알 수 있다.

 

기본적으로 차트라고 하는 것은 데이터가 n개이므로 데이터를 배열로 만들어 그려보자.

그런데, 위 높이는 알맞게 나올 것 같은데 차트 내부의 Bar 간격을 주지 않아서 겹쳐진 모양으로 그려져 버리게 될 것이다.

아래 사진을 살펴보자.

뭔가 비율이 필요하다.

 

내부 Bar 끼리의 간격을 주려면 특정 방법이 필요하여 보인다.

자세히보니 맨 처음은 기본 최소값을 주면되고, 다음부터는 일정 간격을 주면 될 것 같다.

이를 조금더 풀어보면 아래사진처럼 나타낼 수 있다.

네..그렇습니다. 또 계산입니다.

 

역시 코드로보는게 빠르겠다.

아래 스크립트를 변경하여보자.

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,
    max_y : height * 0.9,
    min_y : height * 0.1
}

ctx.beginPath();
var value = [90, 50, 60];  //사용자가 입력한 값
value.forEach( (data, idx) => {
    //x좌표의 시작은 최소x값에 비율을 더해준 값
    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) ));
});

 

위 내용을 실행하면 나름 간격을 일정하게 둔 차트를 만날 수 있다.

간격이 일정해!!

 

물론, 간격 말고도 넓이 부분도 계산이 필요한 부분인데(내부데이터의 갯수나 차트의 넓이에 따라서) 일단 고정값으로 넘어가자.

차트의 대부분은 애니메이션 효과가 있어야 뭔가 생동감이 난다.

애니메이션 효과를 주려면 setInterval 함수를 사용해서 그리고 지우고를 반복하여 주어야 한다.

먼저 단계를 나누어보자.

1. 차트 데이터가 1에서 출발한다.

2. 데이터를 증가시키면서 그려준다.

3. 만약 데이터가 일정크기에 다다르면 그리지 않는다.

4. 데이터 그리기가 종료되면 interval을 종료한다.

 

그러면 1에서 출발하기 위한 배열을 선언하자.

배열크기는 사용자가 입력한 데이터 크기와 동일해야 한다.

그러면서 위에서 사용한 그리기 방법으로 계속해서 그려주면된다.

var virtualVal = [1, 1, 1];
var interval = setInterval(() => {
    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) ));
        }
    });  
}, 10);

 

코드를 이렇게만 수정하면 setInterval이 멈추지 않으므로 계속해서 그림을 그리는 효과가 나온다.

이를위해서 그리기가 완료되었는지 판별한 후 그리기가 완료되면 멈추도록 코드를 수정해야 한다.

var virtualVal = [1, 1, 1];
var interval = setInterval(() => {
    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) ));
        }
    });  
    var chekcer = [false, false, 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){  //1개라도 아직 덜 끝났으면
            breaker = false; //멈추지마!
        }
    });     

    if(breaker){ //전부 멈춤상태면 끝!
        console.log('fin');
        clearInterval(interval);
    }
}, 10);

 

아래는 이에 대한 최종 소스코드이다.

<!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,
        max_y : height * 0.9,
        min_y : height * 0.1
    }

    var value = [90, 50, 60];  //사용자가 입력한 값
    var wid = 50; //넓이

    ctx.beginPath();


    var virtualVal = value.slice().map((arg)=>1);  //가상배열, 초기화

    var interval = setInterval(() => {  //간격을 주면서 그린다.
        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) ));
            }
        });  
        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){ //1개라도 진행중이면!
                breaker = false;  
            }
        });     

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

    
</script>

 

위 내용을 실행하면 아직 이쁘지는 않지만 제법 애니메이션 효과도 있는 차트가 그려지게된다.

그려진다아아~

 

자..이제 첫번째 차트 그리기 단계가 이루어졌다.

다음 시간에는 차트 내부를 조금 더 다듬어보고 사용자가 입력한 데이터를 캔버스에 표출 해 보도록 하자.

 

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

댓글