안녕하세요 :)
오늘은 아래 사진과 같은 ui 작업 도중에 차트가 있는 것을 확인했습니다.
주식 관련 어플에서 흔히 볼 수 있는 차트인데요, 이부분을 구현하는데 약간의 고민이 있었습니다.
어플의 다른 차트 부분은 ui 라이브러리를 이용하고 있는데, 이 부분을 ui 라이브러리를 사용하는 게 맞는지 고민되었습니다.
왜냐하면 ui 라이브러리를 이용하면 아래와 같은 단점이 있기 때문입니다.
- 렌더링 부담
- 비대한 로딩 용량
- 디자인 유연성 하락
- 라이브러리 비교 및 테스팅
위의 ui는 목록 형식으로 다량의 ui가 반복해서 호출될건데 되도록이면 가벼운 ui가 좋겠다고 생각되어 svg를 이용해 직접 만들기로 했습니다.
차트를 만드는 방법은 흔히 svg와 canvas를 이용하는 방법이 있는데요, 여기서 svg를 이용한 이유는 다음과 같습니다.
- 유저와의 인터페이스
- 유지보수 용이
svg는 태그로서 dom내에 위치합니다. 따라서 클릭과 같은 이벤트와 연동하기 편리해 유저와의 인터페이스를 구현하는데 장점이 있습니다. 반면 canvas는 안에 드로잉 되는 그림들에 인터렉션을 추가하려면 꽤나 귀찮습니다 ㅠㅠ 그리고 많은 신입 개발자분들이 canvas를 어려워하고 코드가 복잡해지기 때문에 유지보수 측면에서도 svg가 더 용이합니다.
1. 데이터를 Catmull-Rom 알고리즘으로 보간하기
우선 아래의 함수를 이용해 데이터에 보간값을 넣어주어야 합니다. 일반적인 직선 그래프는 그냥 svg를 이용해 직선을 쭉 그으면 그만이지만 곡선이 있는 spline 차트의 경우 각각의 포인트 전후에 보간값을 넣어주어 완만한 그래프가 나오도록 해주어야 합니다.
export default function catmullRom2bezier(data, minValue, xStep, yStep, height) {
const result = [];
for (let i = 0; i < data.length - 1; i++) {
const p = [];
p.push({
x: Math.max(i - 1, 0) * xStep,
y: height - (data[Math.max(i - 1, 0)] - minValue) * yStep
})
p.push({
x: i * xStep,
y: height - (data[i] - minValue) * yStep
})
p.push({
x: (i + 1) * xStep,
y: height - (data[i + 1] - minValue) * yStep
})
p.push({
x: (Math.min(i + 2, data.length - 1)) * xStep,
y: height - (data[Math.min(i + 2, data.length - 1)] - minValue) * yStep
})
const bp = [];
bp.push({
x: ((-p[0].x + 6 * p[1].x + p[2].x) / 6),
y: ((-p[0].y + 6 * p[1].y + p[2].y) / 6)
});
bp.push({
x: ((p[1].x + 6 * p[2].x - p[3].x) / 6),
y: ((p[1].y + 6 * p[2].y - p[3].y) / 6)
});
bp.push({
x: p[2].x,
y: p[2].y
});
result.push(bp);
}
return result;
}
2. SVG path d 특성값 생성하기
그리고 svg path 태그의 d 특성에 넣어줄 값을 아래의 함수로 생성합니다.
여기서 M과 C 그리고 L이 의미하는 바가 무엇인지 이해하면 쉽게 이해되실 겁니다.
function makePath(points) {
var result = "M" + points[0][0].x + "," + points[0][0].y + " ";
for (var i = 0; i < points.length; i++) {
result += "C" + points[i][0].x + "," + points[i][0].y + " " + points[i][1].x + "," + points[i][1].y + " " + points[i][2].x + "," + points[i][2].y + " ";
}
return result;
}
function makeColorPath(points) {
var result = "M" + points[0][0].x + "," + points[0][0].y + " ";
for (var i = 0; i < points.length; i++) {
result += "C" + points[i][0].x + "," + points[i][0].y + " " + points[i][1].x + "," + points[i][1].y + " " + points[i][2].x + "," + points[i][2].y + " ";
}
result += "L" + points[points.length-1][2].x + "," + height + " "
result += "L" + 0 + "," + height + "z";
return result;
}
3. svg 태그 생성하기
그리고 아래와 같이 svg 태그를 만들고 데이터를 넣어주면 완성입니다.
return (
<svg width={width} height={height}>
<defs>
<linearGradient id="gradient" gradientTransform="rotate(90)">
<stop offset="5%" stop-color={color} />
<stop offset="55%" stop-color="white" />
</linearGradient>
</defs>
{fillColor ?
<path
d={colorPathData}
stroke="none"
stroke-linejoin="round"
strokeWidth={lineWidth}
fill="url('#gradient')"
/>: null
}
<path
d={pathData}
stroke={color}
stroke-linejoin="round"
strokeWidth={lineWidth}
fill="none"
/>
</svg>
);
참조: https://advancedweb.hu/plotting-charts-with-svg/
Plotting charts with SVG
This post is about drawing Catmull-Rom splines with SVG
advancedweb.hu
'Front-End > SVG' 카테고리의 다른 글
[SVG] SVG를 이용한 gauge 차트 만들기 (0) | 2023.08.01 |
---|