[ChartJS / react-chartjs-2] mix형(bar/line) 차트 만들어보기 #4

김하정·2024년 1월 2일
1
post-thumbnail

다른 차트 형태들은 예시가 많이 올라와있어서 비교적 예시가 적은 믹스형 차트에 대해 알아보고, 클릭 이벤트 등등을 적용해보고자 한다.

options 와 그 외 기본적인 세팅들은 앞선 포스팅들을 참고해주면 좋다!

먼저 예시 데이터를 어떤걸로 할지 고민하다가 npm trends 사이트의 차트를 응용해보기로 하였다!
react vs vue - npm trends 링크 참고

npm trends 를 참고하여, react / vue 에 대한 line 차트와
두가지의 합계를 나타내는 총 합계에 대한 bar 차트를 혼합하여 표현하고자 한다.

🍄 Mix 형 더미 데이터 넣기

기본적인 데이터 구조는 다음과 같이 잡았다.

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

현재까지 잡힌 화면 ⭐️

🍄 options 추가해보기

1️⃣ labels 의 간격을 최대 간격 지정해주기 (maxTicksLimit)

실무에서 쓰다보면, 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 가 고정되었단 것을 알 수 있다.

2️⃣ y축의 높이를 컨트롤 해보자!

이번에는 한번 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가 최대 몇부터 최소 몇까지 들어올지 모르기 때문이다!

☀️ 데이터의 max 값과 min 값을 활용하여 stepSize를 유동적으로 잡아주는 함수를 만들어보자!

데이터가 들어 올 때마다, 데이터의 최대값, 최소값을 받아 간격을 잡아주는 함수를 만들어볼 수 있다.

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;
};
profile
web developer

0개의 댓글