이번에는 c3.js를 사용하여 그래프를 다루는 방법에 대해서 알아보도록 하겠습니다. 우선 c3.js는 d3.js에 의존적인 라이브러리 입니다.
우선 c3는 다음을 의미합니다
1. Comfortable
2. Customizable
3. Controllable
d3는 다음을 의미합니다
Data-Driven Document
이 둘의 용어를 비교하면 c3.js가 먼가 더 편안한 느낌이 듭니다. 제 개인적으로도 c3.js가 사용하기 좀더 편한 느낌을 받았습니다.
c3.js를 사용하기 위해서는 js파일과 css 파일을 가져와야 합니다.
● 라이브러리 가져오기
css파일과 js파일을 가져와야 합니다. 해당 파일을 가지고 있지 않다면 CDN 서버에서 파일을 가져와도 됩니다.
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.18/c3.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.11/c3.min.js"></script>
<script src="https://d3js.org/d3.v3.min.js"></script>
cs.js는 압축파일과 압축하지 않은 파일모두 제공을 합니다. c3를 사용하기 위해서는 d3가 있어야 합니다.
● 차트 생성하기
<body>
<div id="chart"></div>
<script>
var chart = c3.generate({
bindto: '#chart',
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25]
]
}
});
</script>
</body>
실행결과입니다. 간단한 코드를 이용하여 그래프를 그려보았습니다. 하지만 c3.js는 해당 그래프에 마우스를 올린다면 다음과 같이 정보를 표현합니다.
각 데이터가 어떤 값을 가지고 있는지 보여줍니다. 이 부분 때문에 css 파일을 로드합니다.
● generate 메소드 알아보기
generate 메소드를 호출하여 우리가 만들고자하는 그래프를 정의할 수 있습니다.
var chart = c3.generate({
bindto: '#chart',
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25]
]
}
});
· bindto
bindto는 그래를 그리는 위치입니다.
· 데이터 셋
data는 그래프에 표시될 데이터들입니다. data는 다양한 키들로 구성되어 있습니다.
※columns
columns키는 그래프에 그려지는 데이터 셋 입니다. columns는 중첩된 리스트의 형태로 이루어져 있습니다. 내부에 있는 리스트들은 독립적인 데이터가 됩니다. data1에 대한 데이터, data2에 대한 데이터 더 추가를 하고 싶다면 리스트를 더 추가해주면 그래프가 추가적으로 그려집니다.
※ json
중첩된 리스트 형태로 작성될 필요는 없습니다. 개인적으로 중첩된 리스트의 형태보다는 다음과 같은 형태를 더 선호합니다.
var chart = c3.generate({
bindto: '#chart',
data:{
json:{
data1: [ 30, 200, 100, 400, 150, 250],
data2: [50, 20, 10, 40, 15, 25]
}
}
});
columns 대신 josn 을 활용하면 데이터를 json의 형태로 넣게 됩니다. 개인적으로는 이 방법을 더 선호합니다.
data는 json, columns와 같은 데이터 셋 이외의 다양한 그래프 설정을 해주는 키값들이 존재합니다.
※ type, types
type, types는 어떤 형태로 그래프를 그려줄지에 대한 정의입니다.
var chart = c3.generate({
bindto: '#chart',
data:{
json:{
data1: [ 30, 200, 100, 400, 150, 250],
data2: [50, 20, 10, 40, 15, 25]
},
types:{
data1: 'bar',
data2: 'line'
}
}
});
data1에 해당하는 그래프는 막대 그래프, data2에 해당하는 그래프는 선을 이용하여 표현합니다.
짜짠! 잘 그려졌습니다. 자 그런데 만약 데이터가 5종류가 되는데 하나만 다른 그래프고 나머지는 같은 그래프라면 일일히 다 설정을 해주어야 할까요? 아닙니다. 이땐 type을 적절히 이용하면 간단하게 코드를 작성할 수 있습니다.
var chart = c3.generate({
bindto: '#chart',
data:{
json:{
date: ['20171101', '20171102', '20171103', '20171104', '20171105', '20171106'],
data1: [30, 200, 100, 400, 150, 250],
data2: [50, 20, 10, 40, 15, 25],
data3: [150, 120, 110, 140, 115, 125]
},
x: 'date',
type: 'line',
types:{
data1: 'bar',
}
}
});
type을 line으로 설정했습니다. 이것은 기본적으로 line의 형태로 그래프를 그리라는 것 입니다. 하지만 types에서 data1은 bar그래프로 그리라고 설정했기 때문에 data1에 대한 그래프만 bar 그래프로 표현합니다.
멋진 그래프가 그려졌습니다.
※ x
다음으로는 x 축에 대한 정보입니다. 만약 x축이 1, 2, 3, 4, 5와 같은 연속적인 숫자가 아닌 특정 데이터라면 어떻게 해야할까요? 이때는 x를 이용합니다.
var chart = c3.generate({
bindto: '#chart',
data:{
json:{
date: ['20171101', '20171102', '20171103', '20171104', '20171105', '20171106'],
data1: [30, 200, 100, 400, 150, 250],
data2: [50, 20, 10, 40, 15, 25],
data3: [150, 120, 110, 140, 115, 125]
},
x: 'date',
type: 'line',
types:{
data1: 'bar',
}
}
});
x축을 date를 이용하여 설정하겠다는 의미입니다.
· 격자표시
grid를 이용하면 그래프에 격자표시를 할 수 있습니다.
var chart = c3.generate({
bindto: '#chart',
data:{
json:{
date: ['20171101', '20171102', '20171103', '20171104', '20171105', '20171106'],
data1: [30, 200, 100, 400, 150, 250],
data2: [50, 20, 10, 40, 15, 25],
data3: [150, 120, 110, 140, 115, 125]
},
x: 'date',
type: 'line',
types:{
data1: 'bar',
}
},
grid: {
x: {
show: true
},
y: {
show: true
}
}
});
x축과 y축 show의 값에 따라서 특정 축의 격자표시만 할 수 있습니다.
· 축 설정
axis를 이용하여 축을 설정 할 수 있습니다.
var chart = c3.generate({
bindto: '#chart',
data:{
json:{
date: ['20171101', '20171102', '20171103', '20171104', '20171105', '20171106'],
data1: [30, 200, 100, 400, 150, 250],
data2: [50, 20, 10, 40, 15, 25],
data3: [150, 120, 110, 140, 115, 125]
},
x: 'date',
type: 'line',
types:{
data1: 'bar',
}
},
axis: {
x: {
tick: {
format: function(d) { console.log(d); return d}
}
},
y: {
tick: {
format: function(d){ return d}
}
}
},
grid: {
x: {
show: true
},
y: {
show: true
}
}
});
axis는 내부적으로 x와 y의 키값을 가지고 있습니다. 즉, x축과 y축 각각 설정이 가능합니다. tick은 축에 찍히는 값을 의미합니다. tick은 format이라는 키를 가지고 있습니다. format을 통해서 각 축에 데이터를 어떤식으로 띄울지 설정할 수 있습니다.
axis: {
x: {
tick: {
format: function(d) { return `${d.toString().slice(0, 4)}년`;}
}
},
y: {
tick: {
format: function(d){ return d}
}
}
}
format에 콜백 함수를 설정해도 되지만 데이터를 좀만 수정하여 넣는다면 귀찮게 toString이나 slice와 같은 함수를 사용하지 않아도 됩니다.
json:{
date: ['2017-11-01', '2017-11-02', '2017-11-03', '2017-11-04', '2017-11-05', '2017-11-06'],
data1: [30, 200, 100, 400, 150, 250],
data2: [50, 20, 10, 40, 15, 25],
data3: [150, 120, 110, 140, 115, 125]
},
axis: {
x: {
type:'timeseries',
tick: {
count: 1,
format: '%Y'
}
},
y: {
tick: {
format: function(d){ return d}
}
}
}
y축은 다른 형태로 수정해보겠습니다.
axis: {
x: {
type:'timeseries',
tick: {
count: 1,
format: '%Y'
}
},
y: {
tick: {
format: d3.format("$,")
}
}
}
● 다양한 차트의 형태 -1
data:{
json:{
date: ['2017-11-01', '2017-11-02', '2017-11-03', '2017-11-04', '2017-11-05', '2017-11-06'],
data1: [30, 200, 100, 400, 150, 250],
data2: [50, 20, 10, 40, 15, 25],
data3: [150, 120, 110, 140, 115, 125]
},
x: 'date',
types:{
data1: 'spline',
data2: 'area',
data3: 'scatter'
}
}
● 다양한 차트의 형태 - 2
data:{
json:{
date: ['2017-11-01', '2017-11-02', '2017-11-03', '2017-11-04', '2017-11-05', '2017-11-06'],
data1: [30, 200, 100, 400, 150, 250],
data2: [50, 20, 10, 40, 15, 25],
data3: [150, 120, 110, 140, 115, 125]
},
x: 'date',
type: 'pie'
}
● 다양한 차트의 형태 - 2
data:{
json:{
date: ['2017-11-01', '2017-11-02', '2017-11-03', '2017-11-04', '2017-11-05', '2017-11-06'],
data1: [30, 200, 100, 400, 150, 250],
data2: [50, 20, 10, 40, 15, 25],
data3: [150, 120, 110, 140, 115, 125]
},
x: 'date',
type: 'donut'
}
● 차트의 혼합
var chart = c3.generate({
bindto: '#chart',
data: {
columns: [
['data1', 30, 20, 50, 40, 60, 50],
['data2', 200, 130, 90, 240, 130, 220],
['data3', 300, 200, 160, 400, 250, 250],
['data4', 200, 130, 90, 240, 130, 220],
['data5', 130, 120, 150, 140, 160, 150],
['data6', 90, 70, 20, 50, 60, 120]
],
type: 'bar',
types: {
data3: 'spline',
data4: 'line',
data6: 'area',
},
groups: [
['data1','data2']
]
},
grid: {
x: {
show: true
},
y: {
show: true
}
}
});
● 이벤트 설정
data: {
columns: [
['data1', 30, 20, 50, 40, 60, 50],
['data2', 200, 130, 90, 240, 130, 220],
['data3', 300, 200, 160, 400, 250, 250],
['data4', 200, 130, 90, 240, 130, 220],
['data5', 130, 120, 150, 140, 160, 150],
['data6', 90, 70, 20, 50, 60, 120]
],
type: 'bar',
types: {
data3: 'spline',
data4: 'line',
data6: 'area',
},
groups: [
['data1','data2']
],
oninit: function(d){console.log('init')},
onmouseover: function(d){console.log('onmouseover'); console.log(d)},
onmouseout: function(d){console.log('onmouseout'); console.log(d);},
onresize: function(d){console.log('onresize'); console.log(d);},
onresized: function(d){console.log('onresized'); console.log(d);},
onrendered: function(d){console.log('onrendered'); console.log(d)}
}
이벤트 부분은 자바스크립트를 좀 써봤다면 어느정도 느낌이 올 것이다. 해당 코드를 직접 실행하면서 콘솔창을 확인하면 바로 알 수 있는 부분입니다.
그래프에서 마우스를 이리저리 옮기면 해당 그림처럼 출력되는것을 확인할 수 있습니다.
문서를 보면 oninit, onresize, onresized, onrendered는 차트가 생성되거나 화면 사이즈가 변하는 중, 사이트가 완전히 변했을 때 이벤트가 발생한다고 되있는데, 필자의 컴퓨터에서는 정상적으로 작동이 되지않았습니다. mouseover와 mouseout만 정상작동 되네요 ㅋㅋㅋ
★ 실습
<script>
let chart1;
let chart2;
let chart1Map = new Map();
let chart2Map = new Map();
$(function(){
// 초기화
/*
$('#header').load("layout/header.html");
$('#navbar').load("layout/navbar.html");
$('#footer').load("layout/footer.html");
*/
// 차트 01
chart1 = c3.generate({
bindto: "#item-graph01",
size: {
height: 250
},
data: {
columns: [
["요청", 0, 0, 0, 0, 0],
["완료", 0, 0, 0, 0, 0],
],
types: {
완료: "line", // bar or line 으로 차트 형태 설정
요청: "line",
},
colors: {
완료: "#ffe082",
요청: "#ffc000",
},
labels: {
format: {
// 매입: function (v, id, i, j) { return v + "건"; },
// 요청: function (v, id, i, j) { return v + "건"; },
}
},
groups: [
['완료', '요청']
]
},
tooltip: {
contents: function (d, defaultTitleFormat, defaultValueFormat, color) {
var $$ = this, config = $$.config,
titleFormat = config.tooltip_format_title || defaultTitleFormat,
nameFormat = config.tooltip_format_name || function (name) { return name; },
valueFormat = config.tooltip_format_value || defaultValueFormat,
text, i, title, value, name, bgcolor;
for (i = 0; i < d.length; i++) {
if (! (d[i] && (d[i].value || d[i].value === 0))) { continue; }
if (! text) {
title = titleFormat ? titleFormat(d[i].x) : d[i].x;
text = "<table class='" + $$.CLASS.tooltip + "'>" + (title || title === 0 ? "<tr><th colspan='2'>" + title + "</th></tr>" : "");
}
name = nameFormat(d[i].name);
value = valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index);
bgcolor = $$.levelColor ? $$.levelColor(d[i].value) : color(d[i].id);
text += "<tr class='" + $$.CLASS.tooltipName + "-" + d[i].id + "'>";
text += "<td class='name'><span style='background-color:" + bgcolor + "'></span>" + name + "</td>";
text += "<td class='value'>" + value + "건 </td>";
text += "</tr>";
}
//console.log('d -> ' + d);
//chart1Map.set(innerData.index + "_buy_cnt" , innerData.buyCnt);
//chart1Map.get()
/*
text += "<tr class='total'>";
text += "<td class='name'>" + '매입' + "</td>";
text += "<td class='value'>" + '9,990' + "건 </td>";
text += "</tr>";
text += "<tr class='total'>";
text += "<td class='name'></td>";
text += "<td class='value'>" + '9,990' + "원 </td>";
text += "</tr>";
*/
return text + "</table>";
}
},
// jyj 수정=============== responsive: false, 추가 (가로줄 확장)
responsive: false,
bar: {
width: {
ratio: 0.4,
},
},
transition: {
duration: 1000
},
padding: {
bottom: 0
},
axis: {
x: {
show: true,
type: "category", //그룹 막대일때 지정 밑 categories에서 x축 칼럼을 설정할수 있음
// categories: ['1st', '2nd', '3rd', '4th', '5th'],
// categories: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
// 11, 12, 13, 14, 15, 16, 17, 18, 19,
// 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,31],
categories: [1,"" ,"" ,"" , 5,"" , "", "", "", 10,
"", "", "", "", 15, "", "", "", "",
20, "", "", "", "", 25, "", "", "", "", 30,""],
min: 0
},
y: {
show: false,
max: 100,
min: 0,
padding: {
bottom: 0
}
}
},
legend: {
show: false
}
});
// 차트 02
chart2 = c3.generate({
bindto: "#item-graph02",
size: {
height: 250
},
data: {
columns: [
["판매중", 0, 0, 0, 0, 0],
["매각", 0, 0, 0, 0, 0],
],
types: {
매각: "line",
판매중: "line",
},
colors: {
매각: "#82d8a9",
판매중: "#00b050",
},
labels: {
format: {
// 매입: function (v, id, i, j) { return v + "건"; },
// 요청: function (v, id, i, j) { return v + "건"; },
}
},
groups: [
['매각', '판매중']
]
},
tooltip: {
format: {
title: function (d) { return 'data ' + d; },
value: function (value, ratio, id) {
//console.log("" + value + " , " + ratio + " , " + id);
var format = id === '매각' ? '건' : '건';
return value + format;
},
}
},
//responsive: false, 추가 (가로줄 확장) 크기 설정 가능
//ex <div class="graph" id="item-graph01" width="500vw" height="500vh"></div>
// vw,vh 는 반응형일때 아니라면 그냥 px
responsive: false,
bar: {
width: {
ratio: 0.4,
},
},
axis: {
x: {
show: true,
type: "category", //그룹 막대일때 지정 밑categories에서 x축 칼럼을 설정할수 있음
categories: [1,"" ,"" ,"" , 5,"" , "", "", "", 10,
"", "", "", "", 15, "", "", "", "",
20, "", "", "", "", 25, "", "", "", "", 30,""],
min: 1 //최소값 설정
},
y: {
show: false,
max: 100,
min: 0, //최소값 설정
padding: {
bottom: 0
}
}
},
legend: {
show: false
},
transition: {
duration: 1000
},
});
/*
setTimeout(function () {
chart1.load({
columns: [
["요청", 20, 30, 50, 60, 70],
["완료", 30, 35, 65, 80, 85],
],
});
chart2.load({
columns: [
["판매중", 20, 30, 50, 60, 70],
["매각", 30, 35, 65, 80, 85],
],
});
}, 300);
*/
Service.getC3Data();
$(".datepicker").datepicker({
showOn: "both",
buttonImage: "/assets/img/date_range.png",
buttonImageOnly: true,
dateFormat: 'yy-mm-dd',
monthNames: ['1월','2월','3월','4월','5월','6월','7월','8월','9월','10월','11월','12월'],
dayNamesMin: ['일','월','화','수','목','금','토'],
dayNames: ['일요일','월요일','화요일','수요일','목요일','금요일','토요일'],
onSelect: function() {
searchDate = $.datepicker.formatDate("yy-mm-dd",$("#datepicker").datepicker("getDate"));
// alert(searchDate);
console.log(searchDate);
}
});
$('#btnSearch').click(function () {
Service.getC3Data_new();
});
});
function getToday(){
var date = new Date();
var year = date.getFullYear();
var month = ("0" + (1 + date.getMonth())).slice(-2);
var day = ("0" + date.getDate()).slice(-2);
return year + "-" + month + "-" + day;
}
let chart1_requestList = [];
let chart1_finishList = [];
let chart2_sellingList = [];
let chart2_sellList = [];
let obj = {};
let searchDate = getToday();
let Service = {
getC3Data: function () {
var url = "/api/admin/v1/dashboard";
var ajaxParam = {
"url": url,
"type": "GET",
"data": ""
}
Common.ajaxJSONV2(ajaxParam, function (data) {
if (!data) {
alert('return data error');
return false;
}
if (data.result != 0) {
alert(data.message);
return;
} else {
//alert(data.message);
chart1_requestList.push('요청');
chart1_finishList.push('완료');
chart2_sellingList.push('판매중');
chart2_sellList.push('매각');
console.log(data.data.weekly);
for (let i = 0; i < data.data.weekly.length; i++) {
let innerData = data.data.weekly[i];
/*
chart1_requestList.push((i+1) * 10);
chart1_finishList.push((i+1) * 10);
chart2_sellingList.push((i+1) * 10);
chart2_sellList.push((i+1) * 10);
*/
chart1_requestList.push(innerData.requestCnt);
chart1_finishList.push(innerData.finishCnt);
chart2_sellingList.push(innerData.sellingCnt);
chart2_sellList.push(innerData.sellCnt);
chart1Map.set(innerData.index + "_buy_cnt" , innerData.buyCnt);
chart1Map.set(innerData.index + "_buy_money" , innerData.buyMoney);
//console.log(innerData.index + ' ' + innerData.requestCnt);
}
console.log('chart1_requestList ' + chart1_requestList);
console.log('chart1_finishList ' + chart1_finishList);
console.log('chart2_sellingList ' + chart2_sellingList);
console.log('chart2_sellList ' + chart2_sellList);
chart1.load({
columns: [
chart1_requestList,
chart1_finishList,
],
});
chart2.load({
columns: [
chart2_sellingList,
chart2_sellList,
],
});
$('#guide_request_cnt').text(Common.comma(Number(data.data.todayGuideRequestCnt)));
$('#guide_finish_cnt').text(Common.comma(data.data.todayGuideFinishCnt));
$('#guide_ing_cnt').text(Common.comma(data.data.todayGuideRequestCnt));
$('#guide_buy_cnt').text(Common.comma(data.data.todayGuideBuyCnt));
$('#guide_buy_money').text(Common.comma(data.data.todayGuideBuyMoney));
$('#guide_selling_cnt').text(Common.comma(data.data.todayGuideSellingCnt));
$('#guide_sell_cnt').text(Common.comma(data.data.todayGuideSellCnt));
$('#guide_sell_money').text(Common.comma(data.data.todayGuideSellMoney));
}
return;
});
},
★ 데이터 형
출처 : https://m.blog.naver.com/pjt3591oo/221143761062
'꿀팁 활용' 카테고리의 다른 글
Nginx 명령어 모음 (0) | 2022.05.11 |
---|---|
[QueryDSL] Expressions.dateTemplate !! 혹은 StringTemplate ?? QueryDSL 날짜시간 컬럼 나눠쓰기(split) (0) | 2022.04.13 |
Nginx 파일 업로드 용량 제한 수정 (0) | 2022.03.31 |
vi 에서 문자열 검색 (0) | 2022.03.31 |
[IntelliJ] VCS(Git) 사용 시 느림, 멈춤 현상 조치 방법 (0) | 2022.02.08 |