차트는 Canvas
, DIV(html, css)
, SVG
등 크게 3가지 방법으로 만들 수 있다.
비트맵 영역을 활용
즉, 자바스크립트 코드로 화면 위에 픽셀을 그려 넣는 방법
👎) 픽셀을 다루게 되어 도형의 크기에 따라 해상도를 따로 고려해야 하는 단점이 있다.
DIV로 도넛 차트를 그리는 경우, 원을 나타내기 위해 radius를 조절하여 그리게 됨
👎) 이 방식은 화면의 크기에 상관없이 도형의 크기가 고정되어 있어 반응형에 취약하다는 단점이 있다.
벡터 형식으로 화면의 크기에 따라 그 크기가 유동적으로 변하는 특징이 있음
👍) SVG 태그 중 하나인 <circle>
과 stroke
, stroke-width
등의 속성으로 쉽게 도형을 표현할 수 있다는 장점이 있음!
나는 여기서 반응형에 쉽게 구현할 수 있고, 자료가 많은 svg를 선택해서 개발을 했었다!
도형의 둘레의 dash
와 gap
을 정의하는 패턴으로 도형의 둘레의 stroke
의 길이와 각각의 stroke
사이의 공백의 패턴을 정의한다!
stroke-dasharray="5"
➡ 각 stroke의 길이와 각각의 stroke 사이 공백의 길이를 5로 정의한다는 의미
stroke-dasharray="5 10"
➡ 첫 번째 값은 각 stroke의 길이, 두 번째 값은 각각의 stroke 사이 공백의 길이를 의미
svg의 circle도 이와 동일하다!
❗
svg circle
은stroke-dasharray
패턴이 시계 3시 방향에서 시작한다는 특징이 있다!
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-dasharray
와 stroke-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>
</>
);
})}
전체적인 코드는 아래와 같다!