차트를 표현해주는 라이브러리 중 Chart.js
사용에 대해 정리하는 글이다.
최종목표는 차트를 이미지화 하여 엑셀로 다운로드까지 하는 것이다.
Chart.js
Chart.js
(Chart.js | Open source)는 오픈 소스이며 데이터를 다양한 차트로 시각화 해주는 Javascript 라이브러리 중 하나이다.
Sample Page를 살펴보면 차트의 기본인 Line, Pie, Doughnut, Bar 등이 있다. Chart.js Sample Page
Sample
Chart.js
를 사용하려면 CDN이나 JS를 다운받아 Import 하면된다. Chart.js CDN by jsDelivr
<style>
body {
width: 400px;
height: 400px;
margin: auto;
margin-top: 100px;
}
</style>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.8.0"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.26.0/polyfill.js"></script>
<select onchange="changeChart(this.value)">
<option value="line">라인</option>
<option value="pie">파이</option>
<option value="doughnut">도넛</option>
<option value="bar">막대(세로)</option>
<option value="horizontalBar">막대(가로)</option>
</select>
<canvas id="myChart" width="100" height="100"></canvas>
<script>
let data = {
labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)'
],
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)'
],
borderWidth: 1
}]
};
let options = {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
};
function changeChart(value) {
myChart.destroy();
myChart = new Chart(ctx, {
type: value,
data: data,
options: options,
});
};
const ctx = document.getElementById('myChart'); // getContext('2d') 를 하는 경우가 있는데 없어야 엑셀다운가능
let myChart = new Chart(ctx, {
type: 'line',
data: data,
options: options
});
</script>
위 예제 소스를 실행하면 다음과 같이 Bar
형태 차트가 생성된다.
이 상태로 type
속성만 변경하면 다양하게 적용이 가능하다.
options
에 대해서는 Documents
를 참고하여 적용한다.
legend: {
display: false, // label 숨기기
},
title: {
display: true, // title 표시
text: '# title', // title 명
},
maintainAspectRatio: false, // 비율유지
showAllTooltips: false, // tooltip 항상표시
tooltips: {
enabled: true, // tooltip 표시(기본은 마우스 hover)
},
plugins: {
legend: true, // legend 표시
outlabels: {} // pie, doughnut 때문에 외부 라이브러리 사용(tooltip을 항상 표시 하는 경우 겹쳐서)
},
layout: {
padding: {
left: 25,
right: 25,
top: 0,
bottom: 0
}
}
위는 사용해본 Options
목록이다.
만약 Line
사용 시 아래에 자동으로 채워지는 옵션을 끄고싶다면 다음을 추가한다.
datasets: [{
fill: false,
...
}]
datasets
의 fill
옵션을 false로 수정하면된다.
Doughnut
이나 Pie
를 사용해서 Tooltips
를 항상 표시했더니 다음과 같은 현상이 생겼다.
// tooltip을 렌더링 이후 바로 표시하기 위해서는 다음 소스가 추가되어야함
Chart.plugins.register({
beforeRender: function(chart) {
if (chart.config.options.showAllTooltips) {
// create an array of tooltips
// we can't use the chart tooltip because there is only one tooltip
// per chart
chart.pluginTooltips = [];
chart.config.data.datasets.forEach(function(dataset, i) {
chart.getDatasetMeta(i).data.forEach(function(sector, j) {
chart.pluginTooltips.push(new Chart.Tooltip({
_chart: chart.chart,
_chartInstance: chart,
_data: chart.data,
_options: chart.options.tooltips,
_active: [sector]
}, chart));
});
});
// turn off normal tooltips
chart.options.tooltips.enabled = false;
}
},
afterDraw: function(chart, easing) {
if (chart.config.options.showAllTooltips) {
// we don't want the permanent tooltips to animate, so don't do
// anything till the animation runs atleast once
if (!chart.allTooltipsOnce) {
if (easing !== 1)
return;
chart.allTooltipsOnce = true;
}
// turn on tooltips
chart.options.tooltips.enabled = true;
Chart.helpers.each(chart.pluginTooltips, function(tooltip) {
tooltip.initialize();
tooltip.update();
// we don't actually need this since we are not animating
// tooltips
tooltip.pivot();
tooltip.transition(easing).draw();
});
chart.options.tooltips.enabled = false;
}
}
});
아까 지정한 옵션들 중 showAllTooltips
를 true
로 수정하고 차트를 보면 다음과 같이 나온다.
물론 도넛이나 파이가 아니더라도 라인도 겹치게 표시된다. 여기서는 도넛이나 파이에서만 적용해보려고 한다.
라이브러리 중에 라벨만 외부로 표시해주는 플러그인을 추가한다.
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-piechart-outlabels"></script>
그리고 아까 지정한 옵션들 중 plugins
에 outlabels
안에 다음을 추가한다.
outlabels: {
text: '%l \n %v',
borderRadius: 15,
color: 'black',
stretch: 30,
font: {
resizable: true,
minSize: 12,
maxSize: 18
},
textAlign: "center"
}
해당 라이브러리의 문서는 링크를 참고하기 바란다.chartjs-plugin-outlabels
text
의 기능은 다음과 같다.
%l
:라벨
데이터를 표시%p
:비율
을 표시%v
:값
을 표시\n
:개행
을 표시
결과는 다음과 같다.
이제 파이나 도넛인 경우에 옵션 중 scales
, showAllTooltips
를 미사용하면 된다. scales
는 그래프의 기본선을 말한다.
function chartUpdate(type, options, title) {
if(type == "doughnut" || type == "pie") {
delete options.scales;
options.title.text = '';
options.plugins.outlabels = {
text: '%l \n %v',
borderRadius: 15,
color: 'black',
stretch: 30,
font: {
resizable: true,
minSize: 12,
maxSize: 18
},
textAlign: "center"
};
options.layout.padding.top = 50;
options.layout.padding.bottom = 50;
} else {
options.title.text = title;
options.scales = {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
delete options.plugins.outlabels;
options.layout.padding.top = 50;
options.layout.padding.bottom = 50;
}
}
위는 일부만 사용한 소스이며 changeChart
함수에서 다음을 추가하면 된다.
chartUpdate(value, options, '# title');
위, 아래의 간격은 padding
옵션을 통해 적절히 조절한다.
(파이나 도넛의 경우 타이틀을 표시하는 경우 겹칠 수 있으므로 사용하지 않음)
Export to Excel
위의 예제를 통해 만들어진 차트를 엑셀로 다운로드 해보자.
그러기 위해서는 또 라이브러리를 추가해야한다. exceljs
라는 라이브러리 이다.Excel.js Github / Excel.js Npm
js를 다운받기 위해서는 다음 링크에서 cdn이나 직접 다운로드한다.Excel.js CDN
그리고 파일을 다운로드하는데 도와주기 위한 FileSaver.js
라이브러리를 또 추가한다.FileSaver.js Github
<script src="https://cdnjs.cloudflare.com/ajax/libs/exceljs/4.2.0/exceljs.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.0/FileSaver.min.js"></script>
<style>
#btn_chart_excel_download {
border: 0; background-color: #007f1b; color: white; font-weight: bold; padding: 5px; border-radius: 10px;
}
</style>
<input type="button" value="엑셀 다운로드" id="btn_chart_excel_download" class="btn_excel"/>
$(function() {
$("#btn_chart_excel_download").click(function() {
// 캔버스에 그려진 이미지를 data url로 변환
let base64Image = ctx.toDataURL(1.0);
// excel js 객체 생성
let workbook = new ExcelJS.Workbook();
// 워크시트 생성
let worksheet = workbook.addWorksheet('Sheet');
// 흰 배경을 만들기 위해 셀 병합
worksheet.mergeCells('A1:H25');
// 가상의 파일 읽기
workbook.xlsx.readFile("chartExample.xlsx");
// 이미지 등록
let imageId = workbook.addImage({
base64: base64Image,
extension: 'png',
});
// 병합했던 셀에 이미지 추가 (엑셀 파일 열면 위치 이동가능)
worksheet.addImage(imageId, 'A1:H25');
// 파일 다운로드
workbook.xlsx.writeBuffer().then(function (data) {
let blob = new Blob([data], {type: "application/vnd.ms-excel;charset=utf-8"});
saveAs(blob, "chartReal.xlsx");
});
});
});
엑셀 다운로드 버튼을 만들고 클릭이벤트를 추가한 후 위의 JS 스크립트를 붙여서 실행하면 다음처럼 다운로드가 된다.
다운로드된 엑셀 파일을 열어보면 미리 병합했던 셀에 이미지가 붙여진걸 확인할 수 있다.
미리 값을 정해진 위치에 지정한다면 보고서 형식으로도 만들 수 있을 것같다.
Sample Source
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
width: 400px;
height: 400px;
margin: auto;
margin-top: 100px;
}
#btn_chart_excel_download {
border: 0; background-color: #007f1b; color: white; font-weight: bold; padding: 5px; border-radius: 10px;
}
</style>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.8.0"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.26.0/polyfill.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-piechart-outlabels"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/exceljs/4.2.0/exceljs.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.0/FileSaver.min.js"></script>
<select onchange="changeChart(this.value)">
<option value="line">라인</option>
<option value="pie">파이</option>
<option value="doughnut">도넛</option>
<option value="bar">막대(세로)</option>
<option value="horizontalBar">막대(가로)</option>
</select>
<input type="button" value="엑셀 다운로드" id="btn_chart_excel_download" class="btn_excel" />
<canvas id="myChart" width="100" height="100"></canvas>
<script>
// tooltip을 렌더링 이후 바로 표시하기 위해서는 다음 소스가 추가되어야함
Chart.plugins.register({
beforeRender: function(chart) {
if (chart.config.options.showAllTooltips) {
// create an array of tooltips
// we can't use the chart tooltip because there is only one tooltip
// per chart
chart.pluginTooltips = [];
chart.config.data.datasets.forEach(function(dataset, i) {
chart.getDatasetMeta(i).data.forEach(function(sector, j) {
chart.pluginTooltips.push(new Chart.Tooltip({
_chart: chart.chart,
_chartInstance: chart,
_data: chart.data,
_options: chart.options.tooltips,
_active: [sector]
}, chart));
});
});
// turn off normal tooltips
chart.options.tooltips.enabled = false;
}
},
afterDraw: function(chart, easing) {
if (chart.config.options.showAllTooltips) {
// we don't want the permanent tooltips to animate, so don't do
// anything till the animation runs atleast once
if (!chart.allTooltipsOnce) {
if (easing !== 1)
return;
chart.allTooltipsOnce = true;
}
// turn on tooltips
chart.options.tooltips.enabled = true;
Chart.helpers.each(chart.pluginTooltips, function(tooltip) {
tooltip.initialize();
tooltip.update();
// we don't actually need this since we are not animating
// tooltips
tooltip.pivot();
tooltip.transition(easing).draw();
});
chart.options.tooltips.enabled = false;
}
}
});
</script>
<script>
let data = {
labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
datasets: [{
fill: false,
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)'
],
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)'
],
borderWidth: 1
}]
};
let options = {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
},
legend: {
display: false, // label 숨기기
},
title: {
display: true, // title 표시
text: '# title', // title 명
},
maintainAspectRatio: false, // 비율유지
showAllTooltips: false, // tooltip 항상표시
tooltips: {
enabled: true, // tooltip 표시(기본은 마우스 hover)
},
plugins: {
legend: true, // legend 표시
outlabels: {
text: '%l \n %v',
borderRadius: 15,
color: 'black',
stretch: 30,
font: {
resizable: true,
minSize: 12,
maxSize: 18
},
textAlign: "center"
} // pie, doughnut 때문에 외부 라이브러리 사용(tooltip을 항상 표시 하는 경우 겹쳐서)
},
layout: {
padding: {
left: 25,
right: 25,
top: 0,
bottom: 0
}
}
};
function chartUpdate(type, options, title) {
if (type == "doughnut" || type == "pie") {
delete options.scales;
options.title.text = '';
options.plugins.outlabels = {
text: '%l \n %v',
borderRadius: 15,
color: 'black',
stretch: 30,
font: {
resizable: true,
minSize: 12,
maxSize: 18
},
textAlign: "center"
};
options.layout.padding.top = 50;
options.layout.padding.bottom = 50;
} else {
options.title.text = title;
options.scales = {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
delete options.plugins.outlabels;
options.layout.padding.top = 50;
options.layout.padding.bottom = 50;
}
}
function changeChart(value) {
myChart.destroy();
chartUpdate(value, options, '# title');
myChart = new Chart(ctx, {
type: value,
data: data,
options: options,
});
};
const ctx = document.getElementById('myChart'); // getContext('2d') 를 하는 경우가 있는데 없어야 엑셀다운가능
let myChart = new Chart(ctx, {
type: 'line',
data: data,
options: options
});
$(function() {
$("#btn_chart_excel_download").click(function() {
// 캔버스에 그려진 이미지를 data url로 변환
let base64Image = ctx.toDataURL(1.0);
// excel js 객체 생성
let workbook = new ExcelJS.Workbook();
// 워크시트 생성
let worksheet = workbook.addWorksheet('Sheet');
// 흰 배경을 만들기 위해 셀 병합
worksheet.mergeCells('A1:H25');
// 가상의 파일 읽기
workbook.xlsx.readFile("chartExample.xlsx");
// 이미지 등록
let imageId = workbook.addImage({
base64: base64Image,
extension: 'png',
});
// 병합했던 셀에 이미지 추가 (엑셀 파일 열면 위치 이동가능)
worksheet.addImage(imageId, 'A1:H25');
// 파일 다운로드
workbook.xlsx.writeBuffer().then(function(data) {
let blob = new Blob([data], { type: "application/vnd.ms-excel;charset=utf-8" });
saveAs(blob, "chartReal.xlsx");
});
});
});
</script>
</head>
<body>
</body>
</html>
'Frontend > Javascript' 카테고리의 다른 글
html -> pdf 다운로드 (html2canvas + jsPDF) (1) | 2021.02.26 |
---|---|
HTML5 + HLS JS를 이용한 실시간 스트리밍 영상 재생하기 (4) | 2020.12.10 |
Form 태그 동적으로 만들어서 POST 전송 (Vanilla JS, jQuery) (0) | 2020.12.08 |
Google Web Page Traslation (0) | 2020.11.23 |
실시간 콤마 (0) | 2020.11.17 |