
도넛 차트와 그래프를 만드는 방법?
HTML & CSS, Canvas, SVG??
흔히 차트나 그래프 등을 만들 때 라이브러리를 사용하지만, 나는 라이브러리를 상요하지 않고 구현하고 싶었다.
HTML
,CSS
루 구현하는 방법 일반tag
를 사용해서 구현하는 방법 으로 막대 그래프 같은 경우width
와height
로 기본 모양을 만들고 배열로height
가 그래프의 값이 되고 이러한 방법을 구현 한다. 도넛 차트 같은 경우radius
를 통해 굴절되는 값을 조절하여 구현한다. 이 방식의 단점은 화면의 크기에 상관없이 차트나 그래프가 고정되어 반응형으로 구현하기 어려움이 있습니다.- Canvas로 구현하는 방법
자바스크립트 코드로 화면 위에 픽셀을 그려 넣는 방법인데, 아직
Canvas
를 사용해 본적이 없었고, 단점으로 도형의 크기에 따라 해상도를 고려해야 하는 단점이 있었다. - 마지막으로 SVG로 구현하는 방법으로
SVG
태그는 벡터 형식으로, 화면에 크기에 따라 유동적으로 변한다는 특징이 있습니다. 그리고 viewBox라는 속성으로 반응형도 쉽게 구현할 수 있습니다. 또한 SVG 태그 중에circle
,rect
등의 태그와stroke
,stroke-width
,stroke-dasharray
등의 속성으로 도형을 쉽게 표현할 수 있다는 장점 있어,SVG
태그를 활용해 그래프와 차트를 구현하기로 했습니다.
도넛 차트 만들기
circle, stroke-dasharray
먼저 circle
태그와 stroke-dasharray
속성에 대해서 알아보겠습니다.
circle
태그는 원의 모양을 만드는 태그로 우리가 흔히 div
등으 태그를 활용해서 radius
를 조절해서 원을 만들거나, 모서리 부분을 둥글게 만들지만 circle
태그를 이용하면 원형 모양을 만들 수 있다.
stroke-dasharray
속성은 MDN
공식문서에서 모양의 윤곽을 칠하는 데 사용되는 대시 및 간격의 패턴을 정의하는 표시 속성입니다. 라고 설명해주고 있다.
즉, 도형의 둘레의 선과 선 길이, 선의 간격을 패턴으로 정의합니다.
기본적으로 stroke-dasharray
의 기본 값은 0으로 설정되어 있고, 밑에 예시로 circle
태그를 활용해서 원을 구현했다.
<svg viewBox='0 0 100 100'> <circle cx='50' cy='50' r='45' fill='none' stroke='blue' strokeWidth='10' strokeDasharray='0' /> </svg>

이러한 형태로 원을 그리고 둘레 선을 원하는 길이와 간격으로 패턴을 정할 수 있다.
위에 사진에 예시를 통해 값이 10
이라는 것은 선의 길이와 간격이 모두 10
으로 설정한다는 의미이고, 오른쪽 상단에 10 5
라는 값은 선의 길이는 10
이고 선의 간격이 5
를 의미한다. 길이와 간격을 입력할때 ,
를 사용해도 된다.
여기서 만약에 strokeDasharray="50 10"
이러한 형태로 적용한다면, 도형의 길이까지만 적용되고, 길이를 넘어서면 잘리게 됩니다.
또한, 주의할 점은 circle
태그의 stroke-dasharray
패턴은 시계 3시 방향으로 시작한다는 특징이 있습니다.
stroke-dashoffset
이제 도넛 모양의 차트를 만들건데 stroke-dasharray
만큼 중요한 것이 stroke-dashoffset
이다, offset
은 css에서 위치를 설정할 때 쓰는 속성같은건데, offset
은 상대 주소의 개념으로 위치를 이동할 기준이 필요하다.
circle
태그 같은경우 stroke-dasharray
가 시계 3시반향에서 시작되니 기본 값으로 3시 방향이 기준으로 offset
을 설정해서 위치를 조정할 수 있다.
이 stroke-dashoffset
를 이용해서 우리가 흔히 생각하는 도넛차트의 모양으로 만들어 줄것이다.
구현해보기

<svg viewBox='0 0 300 300'> <circle cx='100' cy='100' r='90' fill='none' stroke='beige' strokeWidth='15' /> <circle cx='100' cy='100' r='90' fill='none' stroke='blue' strokeWidth='15' strokeDasharray={`${2 * Math.PI * 90 * 0.25} ${ 2 * Math.PI * 90 * (1 - 0.25) }`} strokeDashoffset={2 * Math.PI * 90 * 0.25} /> </svg>
위 도넛 차트는 파란색 값이 25%
되는 차트를 구현했습니다. 현재 두개의 circle
태그를 사용해서 첫번째 circle
태그는 배경을 역할을 하고, 두번째 circle
태그가 차트의 값을 보여줍니다.
여기서 중요한 것은 stroke-dasharray
값과 stroke-dashoffset
입니다. 공통적을 쓰고 있는 2 * Math.PI * 90
이값은 우리가 학창시절에 배운 원의 둘레를 구하는 공식입니다. circle
속성안 r
이라는 값이 반지름입니다.
그리고 stroke-dasharray
는 선의 길이와 선의 간격을 설정한다고 했을 때 2 * Math.PI * 90 * 0.25
이 값이 둘레의 25%
값이고, 2 * Math.PI * 90 * (1 - 0.25)
이값이 75%
만큼 간격을 넓혀서 하나의 선으로만 보여주게 하는 것입니다.
마지막 stroke-dashoffset
의 값은 2 * Math.PI * 90 * 0.25
으로 설정되어 있는데, stroke-dashoffset
은 선의 위치를 조정한다고 했고, circle
태그의 특징으로 시계 3시방향에서 시작한다고 했을때 360도
의 25%
인 90도
만큼 뒤로 올려주어 12시 부터 시작할 수 있게 해줍니다. 반대로 -
값으로 하면 앞으로 이동하겠지요. 이렇게 도넛차트를 구현할 수 있습니다.
도넛차트 정리
stroke-dasharray
은 선의 길이와 선의 간격을 조절할 때 사용하는 속성이고, 도넛 차트를 만들 때 보여줄 값을 25%라고 한다면2 * Math.PI * r * 0.25
으로 선의 길이를 적용하고 그 간격을 조절 할때 나머지 값인2 * Math.PI * r * 0.75
이렇게 적용하면25%
만큼 선이 있고 나머지 간격이75%
만큼 있는 모양이 된다. 간단하게 공식으로 만들면A
라는 값이 우리가 도넛 차트에 보여줄 값이라면stroke-dasharray="(2 * Math.PI * r * A) (2 * Math.PI * r * (1-A))"
이런 식으로 간단하게 만들 수 있다.
stroke-dashoffset
는 선의 위치를 조정하고+
일때 뒤로가고,-
일때 앞으로 이동한다. 역시2 * Math.PI * r * 원하는 각도
로 값을 설정해주면 된다.circle
태그의 특징으로 시계 3시 방향에서 시작하니2 * Math.PI * r * 0.25
으로 하면 12시 방향에서 시작한다.
막대 그래프 만들기
rect, hight, x, y
막대 그래프를 만들기 위해서 SVG태그 안에 rect
라는 태그를 활용해서 구현을 할겁니다. MDN
에서

<svg width='100%' style={{ margin: "50px" }} height='300'> <rect width='40' height='300' fill='blue' y='0' x='0' /> <rect width='40' height='300' fill='blue' y='20' x='100' /> <rect width='40' height='300' fill='blue' y='70' x='200' /> <rect width='40' height='300' fill='blue' y='30' x='300' /> <rect width='40' height='300' fill='blue' y='10' x='400' /> <rect width='40' height='300' fill='blue' y='60' x='500' /> </svg>
위 사진과 코드를 보고 설명할게요. 앞서 설명한 것처럼 rect
는 사각형 모양을 만드는 태그입니다. 그 안에 다양한 속성이 있지만 width
, height
, y
, x
속성이 저는 가장 중요하다고 생각해요.
먼저 width
, height
는 사각형의 가로와 세로 길이를 정하고, y
는 y축 기준으로 +
는 아래로 -
위로 이동할 수 있습니다.(이동한다, 그만큼 비워진다 다양하게 표현할 수 있을 것 같네요.)
그리고 x
역시 좌우로 +
오른쪽으로 -
는 왼쪽으로 이동이 가능합니다.
여기서 재밌는 점은 밑에 사진 입니다.

<svg width='100%' style={{ margin: "50px" }} height='1000'> <rect width='40' height='300' fill='blue' y='0' x='0' /> <rect width='40' height='300' fill='blue' y='20' x='100' /> <rect width='40' height='300' fill='blue' y='70' x='200' /> <rect width='40' height='300' fill='blue' y='30' x='300' /> <rect width='40' height='300' fill='blue' y='10' x='400' /> <rect width='40' height='300' fill='blue' y='60' x='500' /> </svg>
코드를 보면 높이값을 1000px
을 주고 있습니다. 참고로 px
은 생략이 가능했습니다. 다시 사진을 다시보면 SVG
의 높이가 1000px
이고 그만큼 rect
태그가 y
값 만큼 내려온 것을 볼 수 있어요.
이런식으로도 막대 그래프를 표현할 수 있을것 같네요.
막대 그래프의 값을 주는 방법
막대 그래프를 구현하는 것은 생각보다 어렵지 않앗습니다. 하지만 여기서 그래프의 값을 어떻게 줘야 원하는 높이의 그래프를 구현할 수 있을까 고민을 하게 됩니다.
이 생각은 도넛 차트에서 힌트를 얻었는데요. stroke-dasharray
에서 선의 길이와 선의 길이의 간격을 조절해서 도넛 모양의 차트를 구현할 때 막대 그래프에서는 height
로 y
을 이용하는데, height
값은 고정이고, y
의 값을 이용해서 원하는 길이의 차트를 구현하려면 만약 원하는 값이 A
일 때, y=(height-A)
이런식으로 높이와 차트에 줄 높이의 차이를 y
값에 적용하면 제가 원하는 모양이 나왔죠.
그리고 또 하나의 문제점은 x
의 간격이 문제 였는데 이 역시도 array
와 index
를 사용해서 구현했습니다.
const barChart = [{y:0},{y:280},{y:230},{y:270},{y:290},{y:240}] <svg width='100%' style={{ margin: "50px" }} height='1000'> {barChart.map((data,index) => ( <rect width='40' height='300' fill='blue' y={300-data.y} x={100*index} /> ))} </svg>
이런식으로 구현하면 좀 더 간편하게 구현할 수 있고, 부가적으로 이 블로그르 처음 이미지에 막대 그래프 하단에 text
도 SVG
태그 중에서 text
라는 태그를 사용해서 구현했습니다.
애니메이션
도넛 차트와 막대 그래프 모두 애니메이션을 구현을 했는데, 제가 생각했을 때 애니메이션 구현 방법은 크게 두가지 인거 같습니다.
- 첫 번째 : 차트나 그래프의 값을 카운터가 올라가는 형식으로 받아서 구현한다.
- 두 번째 : 차트나 그래프 위에
tag
를 추가해 그tag
사라지는 방식으로 구현한다.
첫 번째 방식은 도넛 차트에서 사용하였다. 나는 값을 카운터가 올라가는 형태로 구현을 했는데, 애니메이션으로도 간단하게 구현을 할 수 있었다.
.AnimatedCircle { animation: circle-fill-animation 2s ease; } @keyframes circle-fill-animation { 0% { stroke-dasharray: 0 calc(2 * 3.14 * 90px); } }
이러한 방법으로 stroke-dasharray
에 애니메이션을 추가해서 구현할 수 있었다. 이것을 보고 CSS
의 학습도 아직 부족하다고 생각했다.
두 번째 방법으로 구현하는 것은 막대 그래프에 적용했다.
막대 그래프가 바닥에서 차트가 올라오는 애니메이션을 구현하고 싶었는데, CSS
로는 구현을 하지 못했고, 막대 그래프 위에 하나의 그래프를 더 만들어 그 그래프를 위에서 부터 없어지는 애니메이션을 구현해서, 실제 모습은 막대 그래프가 아래에서 위로 값이 채워지는 애니메이션으로 구현하였다.
뜻밖의 발견 animate

블로그 글을 쓰면서 자료를 찾다가 SVG
태그 안에 자체 적으로 animate
태그를 활용해서 CSS
로 스타일을 주지 않고 애니메이션을 구현하는 방법이 있다는 것을 발견했다..!!
앞서 프로젝트를 진행할 때 당연히 SVG
로 구현하고 CSS
로 애니메이션을 구현했는데, animate
태그가 있다니..
앞서 말했던 애니메이션 구현 방법을 통해서 구현해도 되지만 자체 animate
태그를 활용하는 방법도 좋을 것 같다.
<svg width='150' height='200'> <rect width='100' height='100'> <animate attributeName='y' from='100' to='0' dur='2s' repeatCount='indefinite' /> </rect> </svg>
위에 코드를 보면 rect
태그 안에 animate
태그를 추가할 수 있습니다. 여기서 attributeName
은 어떤 속성값에 애니메이션을 추가 할건지 선택하는 속성입니다. rect
에서는 x
, y
, height
, width
등이 있구요. x
와y
는 위, 아래 방향이구, height
와width
는 속성 그대로 가로, 세로의 길이입니다.
circle
에서 attributeName
속성은 r
, cx
, cy
등이 있습니다. r
은 반지름, cx
와cy
는 위, 아래 방향이라고 생각하면 됩니다.
그 밖에 from
과to
는 시작과 끝의 값을 설정하는 것이고 dur
은 duration
으로 지속시간을 의미합니다. 마지막으로 repeatCount
는 지속 여부 등을 설정할 수 있습니다.
막대 그래프 정리 막대 그래프는
rect
의 사각형 모양의 태그를 이용해서 구현하고,height
,y
값을 통해서, 원하는 값의 높이를 설정하는데,y
값은+
일 때 밑으로 내려가고-
일때 위로 올라간다. 그래서 높이가300px
인rect
태그가 있고, 원하는 값이270
이라면30
만큼만 내려주면 되니까,y=300-270
이런식으로 구현할 수 있고,x
의 값을 통해rect
의 좌우 간격을 조절할 수 있는데 이는 배열을 통해x=50*index
이러한 방식으로 간격을 일정하게 조절할 수 있다.그리고 애니메이션은 크게 두가지 방법이 있는데, 하는 직접
CSS
를 통해 애니메이션을 주는 방법이 있고, 또 하나는SVG
태그 안에animate
태그를 활용해서 애니메이션 효과는 주는 방법이 있다.
회고
점진적 과부하 프로젝트는 라이브러리를 사용하지 않고 구현을 하고 싶었고, 그렇게 SVG
태그를 활용해서 구현 할 수 있었다. 그리고 그래프를 구현할 때 알지 못했던 animate
라는 tag
도 알 수 있어서 나중에 이것도 활용할 수 있을 것 같다.
처음 사용했던 victory.js
라는 차트 라이브러리가 많이 복잡한 이유가 있던거 같다. 직접 구현해보니, 생각보다 어려운 부분도 있었다. 타이머 처럼 도넛 차트가 채워지는 기능도 있었고, 막대 그래프가 아래에서 위로 올라오는 애니메이션도 구현하려고 시간을 많이 썼다. text
태그의 위치 잡는법, 막대 그래프 hover
했을 때 값을 말풍선으로 보여주고, 높이를 맞추는게 어려웠다.
다음에 차트나 그래프를 구현할 때 좀 더 잘해보자!