jineecode
Chart.js (2) 본문
이전 글)
https://jineecode.tistory.com/191
그래프 파트가 많이 안정되었고 chartjs를 적용하면서 겪었던 시행착오를 적어둔다.
차트의 옵션들은 공식 홈페이지에 잘 나와있으나 잘 모르겠다면 이전에 적은 포스팅을 확인해보거나 이것저것 적용해보길 바란다.
제이쿼리를 사용했으며, 아래 두 파일을 적용했다.
<script type="text/javascript" src="js/chartjs/Chart.js"></script>
<script type="text/javascript" src="js/chartjs/chartjs-plugin-datalabels.js"></script>
chartjs에 들어갈 데이터값이 하드코딩되어있다면 간단하지만, API를 사용해 데이터값이 변동된다면 기타 여러가지 처리를 해주어야 한다.
0) html, css
기본 골격은 다음과 같다.
<div class="futureWeatherWrapper graphWrapper">
<div class="chartWrapper">
<div class="chartAreaWrapper">
<canvas
id="temperatureChart"
width="1200"
height="200"
></canvas>
</div>
</div>
</div>
.graphWrapper {
width: 100%;
height: 100%;
overflow-y: hidden;
}
.chartWrapper {
position: relative;
}
.chartAreaWrapper {
overflow-x: scroll;
}
.chartWrapper > canvas {
position: absolute;
left: 0;
top: 0;
pointer-events: none;
}
chartjs는 기본적으로 반응형을 제공해준다.
데이터가 늘어나면 그에 맞게 data 들의 간격이 일정한 비율로 좁아지는데, 반대로 말하면 데이터가 없으면 그래프가 엄청 늘어진다.
시각적으로 별로여서 뷰포트를 넘어서면 스크롤이 생기도록 캔버스를 감싸는 div에 overflow-x 를 적용했다.
JS (아래의 두 코드블럭은 같은 파일 내에 있음)
1) 선언적 함수
$(document).ready(function () {
initChart(); // 글로벌 스타일 차트 함수
weatherCall(); // API
});
// 차트를 담을 전역변수
let temperatureChart;
let solarChart;
let precipitationChart;
let humidityChart;
// 글로벌 스타일
function initChart() {
// chart.js
Chart.defaults.global.tooltips.enabled = false;
Chart.defaults.global.defaultFontColor = "#000000";
Chart.defaults.global.defaultFontFamily = "Noto Sans KR";
Chart.defaults.global.defaultFontSize = 13;
Chart.defaults.global.defaultLineHeight = 1.33;
Chart.defaults.global.events[2] = false;
}
//라인 그래프
function initLineGraph(stringId, config) {
let ctx = document.getElementById(stringId).getContext("2d");
// temp
return new Chart(ctx, {
type: "line",
data: {
labels: [],
datasets: [
{
data: [],
borderWidth: 1,
backgroundColor: "rgb(0, 85, 0)",
borderColor: "rgb(0, 85, 0)",
fill: false,
tension: 0,
datalabels: {
display: true,
align: "top",
anchor: "end",
color: "black",
borderWidth: 2,
padding: {
bottom: 0,
},
},
},
],
},
options: {
tooltip: null,
hover: {
AnimationDuration: 0,
mode: null,
},
responsive: true,
maintainAspectRatio: false,
legend: {
display: false,
onClick: null,
},
scales: {
yAxes: [
{
display: false,
ticks: config,
},
],
xAxes: [
{
gridLines: {
drawBorder: false,
},
ticks: {
padding: 40,
fontSize: 12,
},
},
],
},
layout: {
padding: {
left: 0,
right: 0,
top: 0,
bottom: 0,
},
},
},
});
}
// 막대그래프
function initBarChart(stringId, label, config) {
let ctx = document.getElementById(stringId).getContext("2d");
return new Chart(ctx, {
type: "bar",
data: {
labels: [],
datasets: [
{
label: label,
// 라벨 이름
data: [],
//각 데이터의 값
backgroundColor: "rgb(160, 208, 153)",
//그래프 컬러
borderColor: "rgb(160, 208, 153)",
//그래프의 테두리 컬러
borderWidth: 10,
//테두리 두께
categoryPercentage: 1.0,
hoverBackgroundColor: "rgb(160, 208, 153)",
hoverBorderColor: "rgb(160, 208, 153)",
barPercentage: 0.9,
datalabels: {
display: true,
align: "top",
anchor: "end",
color: "black",
borderWidth: 2,
padding: {
bottom: -10,
},
},
},
],
},
plugins: [ChartDataLabels],
options: {
tooltip: {
enable: false,
},
hover: {
AnimationDuration: 0,
},
responsive: true,
maintainAspectRatio: false,
legend: {
display: false,
},
scales: {
yAxes: [
{
display: false,
gridLines: {
drawTicks: false,
display: false,
},
//y축
ticks: config,
},
],
xAxes: [
{
borderColor: "rgb(160, 208, 153)",
borderWidth: 1,
gridLines: {
drawTicks: false,
},
ticks: {
padding: 10,
},
},
],
},
layout: {
padding: {
left: 0,
right: 0,
top: 0,
bottom: 0,
},
},
},
});
}
// chart를 update 해주는 함수. ajax를 쓴다면 필수불가결입니다.
function addData(chart, label, data) {
//데이터의 갯수에 따라 차트의 길이를 바꿔줍니다.
if (data.length <= 4) {
chart.canvas.parentNode.style.width = "300px";
} else if (data.length > 4) {
chart.canvas.parentNode.style.width = "400px";
}
// label[]을 차트의 라벨로 할당해주는 작업.
chart.data.labels = label;
// data[]를 차트의 data로 할당해주는 작업.
// datasets[0]을 적어주는 이유는 chartjs는 한 차트에 한 묶음 이상의 데이터를 넣어줄 수 있습니다.
// chartjs는 이를 배열로 받아들입니다.
// ex: 한 라벨에 두 개의 막대 그래프가 그려짐
// 제 그래프에는 한 차트에 한 묶음만 그려지는 차트이므로 [0]을 적용해주었습니다.
chart.data.datasets[0].data = data;
// 차트 업데이트
chart.update();
}
2) AJAX
(아래 ajax는 모듈화가 되어 있는 코드. ajax doc에 맞게 참조)
// current temp
function weatherCall() {
ajaxAuthCall(
"GET", //type
url,
null, //data
function (data) { //success
if (data.success && data.data !== "error") {
// 호출에 성공한 data 배열들을 변수에 담아줍니다.
const weatherArr = data.data.data;
const curWeatherMinMax = data.data.label;
let temperatureMinMax = curWeatherMinMax.temperature;
let lightMinMax = curWeatherMinMax.light;
// 데이터를 배열에 넣기 위한 선언. ex: [33.3, 20, 11, ...]
let currentTemp = [];
let currentLight = [];
let currentRain = [];
let currentHumidity = [];
let todayHourTime = [];
// 이전에 차트가 이미 만들어져 있다면 차트가 그려지기 전에 destroy 해줍니다.
if (temperatureChart) {
temperatureChart.destroy();
solarChart.destroy();
precipitationChart.destroy();
humidityChart.destroy();
}
// 그래프의 뼈대를 만듭니다. 앞서 만들어둔 Graph 함수에 매개변수를 넣어 전역변수에 담는 작업입니다.
// 이 작업이 없으면 getContext null이 뜹니다.
temperatureChart = initLineGraph(
"temperatureChart",
{
display: false,
beginAtZero: false,
stepSize: 1,
max: temperatureMinMax.up + 20,
min: temperatureMinMax.down,
},
"temperatureAxis"
);
solarChart = initLineGraph(
"solarChart",
{
display: false,
beginAtZero: false,
max: lightMinMax.up + 20,
min: lightMinMax.down,
stepSize: 10,
},
"solarAxis"
);
precipitationChart = initBarChart(
"precipitationChart",
"강수량",
{
beginAtZero: true,
suggestedMax: 100,
},
"precipitationAxis"
);
humidityChart = initBarChart(
"humidityChart",
"습도",
{
beginAtZero: true,
min: 0,
max: 110,
stepSize: 5,
},
"humidityAxis"
);
//weatherArr 배열을 반복문으로 돌려 빈 배열에 push 해줍니다.
weatherArr.map((item, index) => {
if (weatherArr.length === 9 && index === weatherArr.length - 1) {
todayHourTime.push(item.time.slice(11, 14).replace("00:", "24시"));
//9개
} else {
todayHourTime.push(item.time.slice(11, 14).replace(":", "시"));
}
currentTemp.push(item.temperature);
currentLight.push(item.light);
currentRain.push(item.rain);
currentHumidity.push(item.humidity);
});
// 이렇게 만들어진 차트를 앞서 만든 addData 함수에 넣어줍니다. (차트, 라벨, 데이터)
addData(temperatureChart, todayHourTime, currentTemp);
addData(solarChart, todayHourTime, currentLight);
addData(precipitationChart, todayHourTime, currentRain);
addData(humidityChart, todayHourTime, currentHumidity);
} else { //호출에는 성공했지만 데이터가 없을 경우
alert("날씨 정보를 불러오지 못했습니다.");
}
},
null,
function (err) { //호출에 실패
alert(err);
}
);
}
겪었던 시행착오
1. ajax로 chart를 만들 때 chart.update()를 해주어야 함
2. ajax로 재호출하여 데이터가 바뀔 경우 chart를 destroy() 해주고 다시 update()를 해주어야 함
(차트를 터치하면 이전 데이터가 나타났다 사라지는 현상이 발생할 수 있습니다.)
요즘엔 프레임워크를 많이 쓰는지라 이 라이브러리를 쓰는 사람이 있을지 모르겠지만 도움이 되셨으면 좋겠습니다.
'JS' 카테고리의 다른 글
constructor / prototype / Object.create() / class (0) | 2021.10.28 |
---|---|
type에 따른 할당 (0) | 2021.10.28 |
default parameter / arguments / rest parameter (0) | 2021.10.25 |
Spread Operator 활용법 (0) | 2021.10.25 |
백틱과 함수를 같이 쓰기 (0) | 2021.10.20 |