라이브러리 없이 도넛차트와 라인 차트 만들어보기 : (with React)

나는야 토마토·2022년 3월 29일
5

기능정리🧙

목록 보기
5/8
post-thumbnail
post-custom-banner

차트는 Canvas, DIV(html, css), SVG 등 크게 3가지 방법으로 만들 수 있다.

Canvas

비트맵 영역을 활용
즉, 자바스크립트 코드로 화면 위에 픽셀을 그려 넣는 방법
👎) 픽셀을 다루게 되어 도형의 크기에 따라 해상도를 따로 고려해야 하는 단점이 있다.

DIV를 이용한 방법

DIV로 도넛 차트를 그리는 경우, 원을 나타내기 위해 radius를 조절하여 그리게 됨
👎) 이 방식은 화면의 크기에 상관없이 도형의 크기가 고정되어 있어 반응형에 취약하다는 단점이 있다.

SVG

벡터 형식으로 화면의 크기에 따라 그 크기가 유동적으로 변하는 특징이 있음
👍) SVG 태그 중 하나인 <circle>stroke, stroke-width 등의 속성으로 쉽게 도형을 표현할 수 있다는 장점이 있음!

나는 여기서 반응형에 쉽게 구현할 수 있고, 자료가 많은 svg를 선택해서 개발을 했었다!

도넛 차트🍩

stroke-dasharray

도형의 둘레의 dashgap을 정의하는 패턴으로 도형의 둘레의 stroke의 길이와 각각의 stroke 사이의 공백의 패턴을 정의한다!

stroke-dasharray="5" ➡ 각 stroke의 길이와 각각의 stroke 사이 공백의 길이를 5로 정의한다는 의미
stroke-dasharray="5 10" ➡ 첫 번째 값은 각 stroke의 길이, 두 번째 값은 각각의 stroke 사이 공백의 길이를 의미

svg의 circle도 이와 동일하다!

svg circlestroke-dasharray 패턴이 시계 3시 방향에서 시작한다는 특징이 있다!

stroke-dashoffset

dash array를 렌더링 할 때 offset을 정의하는 속성
이게 무슨 말인지 예시를 통해 알아보쟈!

svg line의 기본 형태는 stroke-dasharray="10 5"이다.
하지만 거기에다가 stroke-dashoffset을 적용하면 할당되는 값에 따라 패턴이 바뀐다.

그림에서 보이는 것과 같이 네모 박스는 실제 화면에서 보이는 영역이라고 두면

  • 양수(➕) 오른쪽으로 패턴 이동
  • 음수(➖) 왼쪽으로 패턴 이동

이제 도넛 차트 직접 만들어보기! 🍩🍩🍩

75%를 표시하고 있는 도넛 차트를 만들고자 한다면, stroke-dasharray={2 * Math.PI * 90 * 0.75}로 표시해야한다.
왜냐하면 원의 둘레 공식인 2 πr에 0.75를 곱해줘야하기 때문이다.

둘레의 길이는 2* Math.PI * 반지름 또는 document.getElementById().getTotalLength() 등으로 구할 수 있다.

⭐ 하지만 circle경우에는 stroke가 3시 방향에서 시작되므로!!!
도형을 회전시키기 위해서는 앞에서 설명한 stroke-dasharraystroke-dashoffset 속성을 활용해야한다!

// React

// x = 원의 전체 둘레 길이
// a = 데이터에 해당하는 원의 둘레 길이 ex) 63% -> x * 0.63

<circle strokeDasharray="<a> <x - a>" strokeDashoffset="<0.25 * x>" />

이와 같이 계산한다면 도넛차트를 만들 수 있다!

라인 차트📈

라인차트를 만들기 위해서는 순위를 나타내는 동그라미 점과 해당 선이 필요하다.
우선 SVG의 <line>에 대해서 알아보면 다음과 같다.


x1과 y1의 속성은 선의 시작을 의미하고, x2와 y2는 선의 끝을 의미한다.
그렇기 때문에 순위에 대한 차트를 구하기 위해서는 현재의 데이터와 다음의 데이터가 필요하다.

아래의 코드를 보면 const data = [1, 4, 2, 3, 4, 5, 5];의 담겨진 순위를 가지고 차트를 표시한다.
이 때 선을 그리기 위해서는 useRank.js로 해당 데이터를 보내줘서 가공을 해야한다.
우선 해당 data의 x,y를 구하기 위해선 아래의 코드처럼 작성을 하면 된다.
여기서 10은 순위의 변동을 잘 나타내주기 위해 x축과 y축에 곱해주었다.
즉 가로는 10, 20, 30, 40, 50 선이 그려지고, 세로는 10, 40, 20, 30, 40, 50, 50 데이터를 나타낸다.

import { useMemo } from "react";

function useRank(data) {
  const arr = useMemo(
    () =>
      data.map((rank, index) => {
        const lineHeight = Math.round(rank) * 10;
        return {
          x: index * 10,
          y: lineHeight
        };
      }),
    [data]
  );

  return [arr];
}

export default useRank;

이제 선의 시작 x1,y1과 선의 끝인 x2, y2를 구하기 위해서 아래의 코드와 같이 작성해야한다.

import { useMemo } from "react";

function useLine(arr) {
  const lines = useMemo(
    () =>
      arr.reduce((result, point, index) => {
        if (index === 0) return [];
        const previous = arr[index - 1];
        const line = {
          x1: previous.x,
          y1: previous.y,
          x2: point.x,
          y2: point.y
        };
        return [...result, line];
      }, []),
    [arr]
  );
  return [lines];
}

export default useLine;

해당 식을 풀이하면 다음과 같다.
reduce(누적값, 현재값, 인덱스)로 3개의 인자를 활용해서 구해야한다.
이 때 값을 출력해보면 point에 line의 객체가 들어가게 된다.
즉, 현재값이 line값이다. 이제 line의 값들이 result로 들어가게 되서 새로운 배열 즉,[{x1,y1,x2,y2}, {}, ...] 이 나오게 된다.
이제 line의 값에 대해 좀 더 알아보면 line의 첫번째 값을 아래와 같이 나오게 된다.

x1: 0
y1: 0
x2: 10
y2: 40

그러니깐 앞의 useRank에서 구한 값 2개가 합쳐진 값이다.
즉, x1,y1은 1위의 x,y의 값이고 x2,y2는 2번째 값인 4위의 값이다.

이제 선을 그리게 되면 해당 순위를 표시하는 점을 그려야한다. useRank에서 구한 x,y값을 circle과 text에 넣어서 위치를 정해준다. 이 때 글자와 동그라미가 겹치면 보기가 좋지 않으므로, 해당 글자의 x축을 1을 더해주면 완성된다!!!!

{points.map(({ x, y }, index) => {
          return (
            <>
              <GraphCircle cx={x} cy={y} r="1" key={index} />
              <Text x={x + 1} y={y}>
                {data[index] + "위"}
              </Text>
            </>
          );
        })}

전체적인 코드는 아래와 같다!


출처) 리액트에서 도넛 차트를 만들어보자!
React 라이브러리 없이 SVG 차트 만들기

profile
토마토마토
post-custom-banner

0개의 댓글