차근차근/JAVA Script

chart.js 그래프 겹침/ 흔들림

예쁜꽃이피었으면 2022. 2. 15. 17:17

[상황]

부트스트랩 템플릿

샘플 화면이라 내 상황과는 다르지만

나의 경우 '바'모양의 그래프가 있고

바를 눌렀을 경우 그 라벨을 가지고 함수를 타서 '라인' 그래프를 그린다.

 

그래프1 - 바

그래프2 - 라인 (바의 특정 데이터의 상세 정보를 그래프로 그림)

* https://www.chartjs.org/docs/2.8.0/

 

Chart.js · Chart.js documentation

No results matching ""

www.chartjs.org

chartjs 2.8버전을 사용했고 

https://www.chartjs.org/docs/2.8.0/general/interactions/events.html

여기보면 이벤트를 사용할 수 있는 방법이 있는데

나는

var chart = new Chart(ctx, {
    type: 'line',
    data: data,
    //options: {
        // This chart will not respond to mousemove, etc
    //    events: ['click']
    //}
    onClick : function(e){
    	var test_label = this.getElementsAtEvent(e)[0]_model.label; //라벨읽어오기
        var test_backgroundcolor = this.getElementsAtEvent(e)[0]_model.backgroundColor; //백그라운드색읽어오기
        fn_test(test);
    }
});

이런 식으로 라벨값을 넘겨서 함수를 실행했다.

 

그래프는 사용자가 날짜를 변경하거나 상세 그래프를 보기 위해 조건을 변경할 때 마다

새로 그려지는데 기존의 데이터가 남아있어서 그런지

마우스를 그래프 위에 올리면

울렁울렁 했다. '-'..설명 불가..


찾아본 방법들..

https://this-is-coding.tistory.com/46

더보기

[chart.js] canvas에 chart 덧씌울 시 이전 chart가 그대로 있는 문제

chart를 새로이 만들어 canvas 요소에 초기화하나 실제론 이전 chart가 지워지지 않고 그대로있는 문제.

hover 이벤트로 마우스를 chart위에서 움직일 시 이전 chart, 현재 chart가 빈번하게 바뀌며 화면에 나옵니다.

발생원인 : 검색 능력이 부족해 원인은 알 수 없었습니다.

new Chart(canvas, config);

짐작하기로는 new 연산자로 인한 새로운 메모리로 할당됨으로써 canvas를 초기화하는게 아닌 새로운 chart가 기존 chart위에 덧씌우듯 표시되는 문제라 사료됩니다.

해결방법 :

canvas 요소를 삭제하고 재생성하여 chart js를 초기화합니다.

var resetCanvas = function(){
  $('#results-graph').remove(); // this is my <canvas> element
  $('#graph-container').append('<canvas id="results-graph"><canvas>');
  canvas = document.querySelector('#results-graph');
  ctx = canvas.getContext('2d');
  ctx.canvas.width = $('#graph').width(); // resize to parent width
  ctx.canvas.height = $('#graph').height(); // resize to parent height
  var x = canvas.width/2;
  var y = canvas.height/2;
  ctx.font = '10pt Verdana';
  ctx.textAlign = 'center';
  ctx.fillText('This text is centered on the canvas', x, y);
};

 

canvas가 삭제되면서 기존 chart 또한 삭제되기에 이전과 같은 문제가 발생하지 않습니다.

출처 - https://stackoverflow.com/questions/24815851/how-to-clear-a-chart-from-a-canvas-so-that-hover-events-cannot-be-triggered

 

How to clear a chart from a canvas so that hover events cannot be triggered?

I'm using Chartjs to display a Line Chart and this works fine: // get line chart canvas var targetCanvas = document.getElementById('chartCanvas').getContext('2d'); // draw line chart var chart = ...

stackoverflow.com

 

ps. destory와 update를 활용하면 더욱 간단히 변경가능합니다.

destory는 말 그대로 기존 chart를 파괴하는 것이고

update는 기존 data만 변경하여 update하는 기능입니다.

https://www.chartjs.org/docs/3.5.0/developers/api.html

 

API | Chart.js

API For each chart, there are a set of global prototype methods on the shared chart type which you may find useful. These are available on all charts created with Chart.js, but for the examples, let's use a line chart we've made. .destroy() Use this to des

www.chartjs.org

 


https://www.chartjs.org/docs/3.5.0/developers/api.html

 

API | Chart.js

API For each chart, there are a set of global prototype methods on the shared chart type which you may find useful. These are available on all charts created with Chart.js, but for the examples, let's use a line chart we've made. .destroy() Use this to des

www.chartjs.org

https://stackoverflow.com/questions/40056555/destroy-chart-js-bar-graph-to-redraw-other-graph-in-same-canvas

 

Destroy chart.js bar graph to redraw other graph in same <canvas>

I am using the Chart.js library to draw a bar graph, it is working fine, but now I want to destroy the bar graph and make a line graph in the same canvas. I have tried these two ways to clear the c...

stackoverflow.com

https://stackoverflow.com/questions/24815851/how-to-clear-a-chart-from-a-canvas-so-that-hover-events-cannot-be-triggered

 

How to clear a chart from a canvas so that hover events cannot be triggered?

I'm using Chartjs to display a Line Chart and this works fine: // get line chart canvas var targetCanvas = document.getElementById('chartCanvas').getContext('2d'); // draw line chart var chart = ...

stackoverflow.com

https://soobakba.tistory.com/23

더보기

Chart.js 그래프 마우스 오버시에 이전 데이터 보이는 경우 (chartjs hover data change)

chart.js로 구현한 그래프


얼마전 Chart.js로 그래프 작업을 하면서 겪었던 황당한 버그였는데 혼자 몇 분간 삽질하다가 구글에 검색해보니 생각보다 간단하고 명확한 이유여서 빠르게 해결할 수 있었다.

한 캔버스에서 일간, 주간, 월간 버튼을 클릭할 때 마다 다르게 보여지게 구현해야해서 하나의 변수에 그래프 데이터를 저장해두고 사용자 이벤트가 발생할 때 마다 해당 변수의  datasets 프로퍼티에 그래프 데이터를 할당해주었는데 그래프의 특정 포인트에 마우스 오버 할 때 마다 이전 데이터가 화면에 나타나는 것이다.

(예를 들어, 월간 클릭된 상태인데 처음에 로드한 일간 데이터 바뀜)

게다가 마우스를 움직이면 또 새로운 그래프로 나왔다가 왔다갔다 함...
처음엔 너무 황당한 버그라서 어디서부터 손을 대야할지도 몰랐는데, 검색하니 바로 나오더라...

내가 참고한 스택오버플로우는 여기

이전 데이터가 나오는 이유는 그래프 객체를 파괴하지 않고 계속 재할당 해줘서인데,
만약 처음에 var chartObj = new Chart(); 이렇게 할당했었다면,
chartObj.destroy()를 통해 이전 데이터를 삭제해주고 다시 할당하면 된다.

그러나 단순히 데이터만 변경하는 것이라면 굳이 destroy() 하지 않고,
config 데이터 재설정 해준뒤 chartObj.update()를 해주면 더 적은 비용으로 이전 데이터 없이 깔끔하게 업데이트가 가능하다.

(사실 업데이트 때마다 새로운 인스턴스 생성해서 할당 해줬는데 왜 이전 데이터가 남아있는것인지는 잘 모르겠다... 아시는 분 있으면 물어보고싶다... 내 생각엔 Chart.js 라이브러리 내에서 같은 캔버스에 할당되는 데이터를 조작하는 중에 이전 것을 물고있는것이 아닌가 짐작해보지만,,, 확실히는 모르겠다.)

나의 경우에는 타입에 따라서 y축의 min/max 값을 변경해줘야 했는데, 이전 객체 파괴없이 update()만 하는 경우엔 min/max 값 재설정이 안되더라...

그래서 만약 기간만 변경했을 경우 update()로, 타입을 변경했을 때는 파괴 후, 재할당 하는 식으로 작업하였다.

밑에는 실제 작업 소스 일부를 첨부하였다.

storeCtrl.chartComponent: null,
drawChart: function(type, period, list) {

        var ctx = document.getElementById("chart-area").getContext("2d");

        var config = {
            type: 'line',
            data: {
                labels: [],
                datasets: [{
                    label: type,
                    data: [],
                    fill: false,
                    borderColor: '#F00'
                }]
            },
            options: {
                responsive: true,
                title: {
                    display: true,
                    //text:'Chart.js Line Chart'
                },
                tooltips: {
                    mode: 'label'
                },
                scales: {
                    xAxes: [{
                        display: true,
                        scaleLabel: {
                            show: true,
                            labelString: 'Month'
                        }
                    }],
                    yAxes: [{
                        display: true,
                        scaleLabel: {
                            show: true,
                            labelString: 'Value'
                        },
                        ticks: {
                            suggestedMin: type == 'TEMPERATURE' ? -20 : 0,
                            suggestedMax: type == 'TEMPERATURE' ? 30 : 100
                        }
                    }]
                }
            }
        };

        if (period == 'day') {
            //1 DAY

        } else if (period == 'week') {
            //1 WEEK
        } else {
            //1 MONTH
        }

        if (storeCtrl.chartComponent == null) {
            storeCtrl.chartComponent = new Chart(ctx, config);
        } else {
            storeCtrl.chartComponent.config = config;
            storeCtrl.chartComponent.update();
        }
},



1. 페이지 로드되면 storeCtrl.chartComponent 객체를 null로 초기화 한다.
2. 서버로부터 그래프 데이터 받아오면 config변수로 그래프 데이터 셋팅.
3-1. 기간 버튼(DAY, WEEK, MONTH) 눌러서 이벤트 발생시 각 상황에 맞게 데이터 처리해주고 storeCtrl.chartComponent.update()를 통해 데이터 업로드.
3-2. 타입 버튼(온도, 습도) 눌러서 이벤트 발생시 storeCtrl.chartComponent.destroy()로 객체 파괴 및 storeCtrl.chartComponent = null 할당 후 drawChart() 함수 호출하여 new Chart()로 새로운 객체 인스턴스 할당.
(객체 파괴 및 null 할당 부분은 위 소스에서 생략되어 있다.)



 

[해결 방법]

html소스 : canvas를 감싸는 div에 id를 줬다.

 

<div class="row">
	<div class="col-xl-6">
		<div class="card mb-4">
			<div class="card-header">
				<i class="fas fa-chart-area me-1"></i>
				Area Chart Example
			</div>
			<div class="card-body" id="div_myAreaChart"> <!--id="div_myAreaChart"추가-->
				<canvas id="myAreaChart" width="100%" height="40"></canvas></div>
		</div>
	</div>
	<div class="col-xl-6">
		<div class="card mb-4">
			<div class="card-header">
				<i class="fas fa-chart-bar me-1"></i> 
				Bar Chart Example
			</div>
			<div class="card-body" id="div_myBarChart"><!--id="div_myBarChart"추가-->
				<canvas id="myBarChart" width="100%" height="40"></canvas></div>
		</div>
	</div>
</div>

js : 그래프 영역을 지우고 새로 그린다.

var viewChart1 = null;
var ctx =  document.getElementById(div_myAreaChart);

function fn_data1(){
	//데이터 읽어오는 부분 작성
        
    if(viewChart1 != null){ // 이미 그래프를 그렸으면
    	$('myAreaChart').remove(); //그래프 그리는 영역 지움
        $('div_myAreaChart').append('<canvas id="myAreaChart" width="100%" height="40"></canvas>');//그래프를 감싸고 있는 div안에 다시 그래프 영역 작성
		
            if(document.getElementById("myAreaChart") != null){ //그래프 그릴 영역이 생겼으면
                ctx = document.getElementById(div_myAreaChart);
                fn_draw_data1(); // 그래프를 그리는 함수
            }

    }else{ //그래프를 처음 그릴 때
    	fn_draw_data1(); // 그래프를 그리는 함수
    } 
    
}//fn_data1() 끝



function fn_draw_data1(){
	
    viewChart1 = new Chart(ctx,{
    	//그래프 작성
    });
    
}//fn_draw_data1()끝

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형