다른 차트 형태들은 예시가 많이 올라와있어서 비교적 예시가 적은 믹스형 차트에 대해 알아보고, 클릭 이벤트 등등을 적용해보고자 한다.
options 와 그 외 기본적인 세팅들은 앞선 포스팅들을 참고해주면 좋다!
먼저 예시 데이터를 어떤걸로 할지 고민하다가 npm trends 사이트의 차트를 응용해보기로 하였다!
react vs vue - npm trends 링크 참고
npm trends 를 참고하여, react / vue 에 대한 line 차트와
두가지의 합계를 나타내는 총 합계에 대한 bar 차트를 혼합하여 표현하고자 한다.
기본적인 데이터 구조는 다음과 같이 잡았다.
import { Chart } from "react-chartjs-2";
import {
ArcElement,
BarElement,
CategoryScale,
Chart as ChartJS,
Legend,
LineElement,
LinearScale,
PointElement,
Tooltip,
registerables,
} from "chart.js";
ChartJS.register(
...registerables,
LinearScale,
CategoryScale,
BarElement,
PointElement,
LineElement,
Legend,
Tooltip,
ArcElement
);
const MixChartExample = () => {
const react_dummy = [10, 10, 10, 10, 20, 20, 20, 20, 30, 30, 30, 30];
const vue_dummy = [10, 30, 10, 10, 10, 20, 30, 30, 20, 20, 20, 30];
const total_dummy = react_dummy.map((item, idx) => item + vue_dummy[idx]);
const labels_dummy = new Array(12).fill(0).map((_, idx) => idx + 1 + "월");
//chart data
const mixData = {
labels: labels_dummy,
datasets: [
{
type: "line" as const,
label: "React" as string,
data: react_dummy,
borderColor: "#DD5353",
backgroundColor: "#DD5353",
borderWidth: 2, // 선의 크기
pointBorderWidth: 1, // point의 크기
tension: 0.1, // line 차트일 경우 선의 휘어짐 정도
},
{
type: "line" as const,
label: "Vue" as string,
data: vue_dummy,
borderColor: "#5F9DF7",
backgroundColor: "#5F9DF7",
borderWidth: 2,
pointBorderWidth: 1,
tension: 0.1, // line 차트일 경우 선의 휘어짐 정도
},
{
type: "bar" as const,
label: "전체" as string,
backgroundColor: "#ddd",
data: total_dummy,
borderColor: "#ddd",
borderWidth: 2,
},
],
};
return (
<div>
<Chart type="bar" data={mixData} />
</div>
);
};
export default MixChartExample;
data에 작성한 옵션들은 아래 링크들을 참고하여 추가할 수 있다.
point style 참고 문서 - ChartJS
Line형 차트 옵션 참고 문서 - ChartJS
현재까지 잡힌 화면 ⭐️
실무에서 쓰다보면, labels가 유동적인 경우가 있다.
예를 들어, 한 차트에서 3개월 - 1개월 - 일주일 간격의 데이터를 보여줘야 하는 상황에서 labels를 있는 그대로 표현해준다면 화면에 내용이 너무 꽉 찰 수가 있다.
화면이 크다면 상관이 없지만, 작은 화면에서 3개월 치의 labels 를 다 보여주기는 어려울 것이다!
chartJS 에서는 이와 같은 옵션을 제공해주고 있다!!
다음과 같이 옵션을 채워줄 수 있다.
const mixOptions = {
scales: {
x: {
ticks: {
maxTicksLimit: 6,
},
},
},
};
// 생략
<div>
<Chart type="bar" data={mixData} options={mixOptions}/>
</div>
이미지를 보면, 최대 6개 까지로 labels 가 고정되었단 것을 알 수 있다.
이번에는 한번 react_flex_dummy를 만들어보고, 해당 데이터를 react 쪽에 넣어보겠다.
const react_flex_dummy = [
10, 10, 1000, 10, 20, 200, 20, 20, 330, 30, 1130, 30,
];
// 생략
const mixData = {
labels: labels_dummy,
datasets: [
{
type: "line" as const,
label: "React" as string,
data: react_flex_dummy,
...
이와 같이 데이터 차이가 1000 이상 날 경우, 다음처럼 chart의 높이가 큰 차이를 내며 표시 될 것이다.
보면... 최대값이 너무너무 클 경우, 다른 데이터들이 바닥에 희미하게 보이는 것을 알 수 있다.. 😢
y축에 대해서도 ticks 옵션에 stepSize를 지정해주면 되는데, labels와 다르게 data의 범위는 매우 유동적이기 때문에 특정 숫자를 지정해주기 애매할 수가 있다...
data가 최대 몇부터 최소 몇까지 들어올지 모르기 때문이다!
데이터가 들어 올 때마다, 데이터의 최대값, 최소값을 받아 간격을 잡아주는 함수를 만들어볼 수 있다.
const getStepSize = (min: number, max: number) => {
const range = max - min; // 최댓값과 최솟값 간의 범위를 계산
const interval = Math.ceil(range / 3); // 범위를 3등분하여 각 구간의 크기를 계산
const exponent = Math.floor(Math.log10(interval)); // 각 구간의 크기에 대한 로그 값을 계산
const factor = Math.pow(10, exponent); // 10의 거듭제곱을 통해 가장 가까운 10의 배수로 구간 크기를 정규화
return Math.ceil(interval / factor) * factor;
};
total_dummy를 이용하여 min 값과 max 값을 정하여 적용해주면 다음과 같이 react_flex_dummy와 react_dummy 에 따라 y축의 간격이 조정되는 것을 볼 수 있다 ⭐️
react_flex_dummy 인 경우
react_dummy 인 경우
전체적인 코드는 다음과 같다!
다음 포스팅에서는 클릭 이벤트를 적용해보겠다!
import { Chart } from "react-chartjs-2";
import {
ArcElement,
BarElement,
CategoryScale,
Chart as ChartJS,
Legend,
LineElement,
LinearScale,
PointElement,
Tooltip,
registerables,
} from "chart.js";
import { useMemo } from "react";
ChartJS.register(
...registerables,
LinearScale,
CategoryScale,
BarElement,
PointElement,
LineElement,
Legend,
Tooltip,
ArcElement
);
const MixChartExample = () => {
const react_dummy = [10, 10, 10, 10, 20, 20, 20, 20, 30, 30, 30, 30];
// const react_flex_dummy = [
// 10, 10, 1000, 10, 20, 200, 20, 20, 330, 30, 1130, 30,
// ];
const vue_dummy = [10, 30, 10, 10, 10, 20, 30, 30, 20, 20, 20, 30];
const total_dummy = react_dummy.map((item, idx) => item + vue_dummy[idx]);
const labels_dummy = new Array(12).fill(0).map((_, idx) => idx + 1 + "월");
// 최대,최소값을 구하는
const min = useMemo(() => {
return Math.min.apply(null, total_dummy);
}, [total_dummy]);
const max = useMemo(() => {
return Math.max.apply(null, total_dummy);
}, [total_dummy]);
const mixOptions = {
scales: {
x: {
ticks: {
maxTicksLimit: 6,
},
},
y: {
ticks: {
color: "#808080",
stepSize: getStepSize(min, max),
},
},
},
};
//chart data
const mixData = {
labels: labels_dummy,
datasets: [
{
type: "line" as const,
label: "React" as string,
data: react_dummy,
borderColor: "#DD5353",
backgroundColor: "#DD5353",
borderWidth: 2, // 선의 크기
pointBorderWidth: 1, // point의 크기
tension: 0.1, // line 차트일 경우 선의 휘어짐 정도
},
{
type: "line" as const,
label: "Vue" as string,
data: vue_dummy,
borderColor: "#5F9DF7",
backgroundColor: "#5F9DF7",
borderWidth: 2,
pointBorderWidth: 1,
tension: 0.1, // line 차트일 경우 선의 휘어짐 정도
},
{
type: "bar" as const,
label: "전체" as string,
backgroundColor: "#ddd",
data: total_dummy,
borderColor: "#ddd",
borderWidth: 2,
},
],
};
return (
<div>
<Chart type="bar" data={mixData} options={mixOptions} />
</div>
);
};
export default MixChartExample;
const getStepSize = (min: number, max: number) => {
const range = max - min; // 최댓값과 최솟값 간의 범위를 계산
const interval = Math.ceil(range / 3); // 범위를 3등분하여 각 구간의 크기를 계산
const exponent = Math.floor(Math.log10(interval)); // 각 구간의 크기에 대한 로그 값을 계산
const factor = Math.pow(10, exponent); // 10의 거듭제곱을 통해 가장 가까운 10의 배수로 구간 크기를 정규화
return Math.ceil(interval / factor) * factor;
};