ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • SVG 왕초보와 함께하는 그래프/차트 만들기 - 1. 도넛 차트
    토막글 2021. 8. 2. 11:10

    안녕하세요 :)

    오늘은 SVG로 도넛 차트를 만들어본 경험을 공유해보려 합니다.

    단지 한, 두 가지 차트를 위해 Chart.js 같은 라이브러리를 사용하는 것은 기술 남용이라고 생각합니다. 이와 비슷한 생각을 가지시고 웹 세계를 떠돌아다니시는 분들이 있다면 나쁘지 않은 자료가 될 것이라 생각합니다 :) 

     

    결과부터 보자면, 왼쪽의 데이터가 오른쪽 차트로 보이게 됩니다.

    구현 순서

    위와 같은 호를 그리기 위한 순서를 요약해 말해보자면 다음과 같습니다. 참고로 말하자면 저는 2번이 고비였습니다. 😅

    1. SVG viewBox 설정
    2. 각 영역을 호로 그려보기
    3. 호로 그린 영역을 fill : none으로 바꾸고 stroke 를 사용해 그리기
    4. 각 영역마다 animation 주기
    5. viewBox 개선 및 hover 시 scale up

     

    1. SVG viewBox 설정

    SVG의 viewBox 속성으로 우리의 도넛 차트가 어떻게 보여질지 결정해야 합니다. viewBox 에 대한 자세한 설명은 해당 링크를 참고하시길 바랍니다. 우리는 아래의 코드부터 바로 보죠 :)

     

    되게 별거 없죠 😅 우린 반지름이 1인 원을 그릴 거기 때문에 해당 viewBox 설정을 해줍니다.

    좌표로 그려보면 다음과 같습니다.

    문제는 이렇게 viewBox를 설정하게 되면 오른쪽 부터 차트가 그려지게 됩니다. 추후에 나오겠지만 우리는 시작 점을 1, 0으로 이동하고 그릴 거거든요! 때문에 viewBox를 -90도 회전시킵니다.

    -90도 회전시킨 viewBox

     

    2. 각 영역을 호로 그려보기

    저는 호를 그리기 위해 path 태그를 사용하였습니다. 때문에 path 태그 기준으로 설명할 것이지만, 자료를 찾다가 circle 태그로도 충분히 가능해 보여 자료를 첨부해드립니다. (더 쉬워 보여요 ㅜ)

     

    path 태그에서 호를 그리는 원리는 다음과 같습니다. 

    path 태그에서는 d 라는 속성을 가질 수 있는데요, 이 속성을 사용해 path 가 어떻게 그려질 지 조작할 수 있습니다.

    d 속성 내의 command는 여러 가지가 있지만 오늘 우리가 호를 그리기 위해 사용할 명령어는 M, L, A 뿐입니다 :)

     

    M (x, y)는 Move 의 약어로, x, y 좌표로 현재 point를 이동시킵니다.

    L (x, y)는 Line의 약어로, 현재 위치한 포인트에서 x, y로 선을 긋습니다.

     

    문제는 A 인데요, A 가 가지는 parameter가 좀 많습니다.

    A (rx ry angle large-arc-flag sweep-flag x y) 는 Arc curve의 약어입니다. 

    rx, ry는 타원의 두 반지름입니다. 우리는 각각 1로 설정하여 원을 만듭니다.

    angle은 x 축을 기준 상대적인 각도입니다. 우리는 0으로 설정합니다.

    large-arc-flag와 sweep-flag는 큰 호인지, 쓸기 방향은 어디로 할 것인지 정하는 인자인데요, 자세한 설명은 아래 사진과 해당 링크를 참고하시길 바랍니다. 우리는 percent가 0.5를 넘어갈 경우, large-arc-flag를 1으로 하며 sweep-flag는 1로 고정합니다.

    출처 : https://a11y.gitbook.io/graphics-aria/svg-graphics/svg-paths-shape

    마지막 x, y는 호가 끝나는 지점입니다!! 이 점을 구하기 위해서 우리는 추억의 삼각함수를 꺼내야 하는데요...

     

     

    흰 선을 참고하시면 됩니다. 우리는 반지름이 1인 원을 그리고 있기 때문에, 우리가 원하는 x, y 좌표를 cos θ, sin θ로 정의할 수 있습니다.

    우리는 삼각함수의 사용을 위해 각도를 radian으로 변경해야 합니다. 그렇다면 해당 radian을 어떻게 구해야 할까요?? 

    원의 360 degree 는 2π radian 으로 환산됩니다. 그 중 우리는 원하는 percentage 만큼 각도를 그리면 되므로,2π * (percentage) 가 우리가 원하는 각도가 됩니다!! 코드로 옮겨보면 다음과 같습니다.

     

    정리해보면, 

    <path d="d="M 1 0 A 1 1 0 0 1 cosθ sinθ L 0 0"" fill="#5758BB">

    다음과 같은 명령어는 (물론 실제 사용할 땐 cos, sin을 환산해주어야 합니다.) 아래와 같이 해석됩니다.

    1. 현재 좌표 (0, 0) 을 1, 0으로 이동시킨다. (M 1 0)
    2. 반지름이 1, 1인 원일 경우, 각도는 0, 큰 호 flag는 0, 쓸기 플래그는 1 이며 호가 끝마칠 지점은 cosθ sinθ 이다. 
    3. cosθ, sinθ 에서 시작했던 점 (0, 0) 으로 선을 긋는다.

    여기까지 이해하신 당신...!! 정말 수고 많으셨습니다.

    이제는 앞선 정보를 바탕으로 호를 그릴 차례입니다!!!

    앞선 내용들을 이해하셨다면 코드 이해에 무리가 없을 것입니다!! 컴파일에 성공하셨다면 아마 이런 차트를 보게 될 것입니다.

    엥 이건 원 차트인데!!?

    지금도 충분히 이쁘지만, 우리가 원한 건 도넛 차트였죠?? 이를 위해 조금 더 코드를 개선해보려 합니다.

     

    3. 호로 그린 영역을 fill : none으로 바꾸고 stroke 를 사용해 그리기

    fill은 그렸던 영역을 모두 색칠하기 때문에, 우리는 이를 사용해 도넛 차트를 만들기는 어렵습니다. 때문에 stroke 속성을 사용합니다. stroke 속성을 사용하면 그리는 선의 테두리를 컨트롤 할 수 있게 됩니다.

    아래와 같이 코드를 수정해보면, 결과값이 다음과 같이 나옵니다.

    11, 12, 13번 줄이 변경되었습니다.

    아마 위 아래가 약간 짤리게 될텐데, 이는 viewBox 때문에 생기는 문제입니다. 5번 파트에서 해결하도록 하겠습니다. 일단 무시해주세요!!

    앞서 색칠한 영역이 없어지고, 우리가 그리고 있는 path의 테두리의 색상이 생겼습니다.

    선의 두께를 조금 더 높여 보면 어떨까요?

     

    12 번째 value가 0.1 -> 0.4로 변경되었습니다.

    다음과 같이 나오게 될겁니다. 그렇다면 L 속성에 의해 돌아오는 선을 없애주면 테두리만 남게 되지 않을까요???

    이를 위해 stroke-dasharray, stroke-dashoffset 속성을 사용합니다. 해당 속성의 빠른 이해를 위해 우아한테크캠프 4기 최현준님의 좋은 자료를 읽어보시길 권장합니다. 아주 빠른 이해가 될 거예요!!

     

    아래 코드와 함께 해당 속성을 설명하겠습니다.

    14, 15번째 줄이 추가되었습니다.

    stroke-dasharray 에 targetRad, targetRestRad가 추가되었습니다.

    현준님의 자료를 이해하셨다면, 해당 속성에 의해 targetRad 는 그려지게 되고, targetRestRad 만큼 stroke가 지워지게 된다는 것을 이해하실 겁니다. 그렇다면 targetRad가 뭘까요?

    우리는 원의 테두리 길이를 알아내기 위해 2πr 을 해야한다는 것을 알고 있습니다. 그렇다면 percentage 만큼 곱해주면 해당 영역의 원 테두리 길이를 알아낼 수 있겠죠! targetRad 는 해당 영역의 길이이고, targetRestRad는 나머지 영역의 테두리입니다.

    여기에 dashoffset 속성을 줘서 영역끼리 약간 떨어져 있게 만듭니다. 저는 요게 더 이쁘더라고요.

    아래는 그 결과입니다!

    감동의 도넛차트

     

    4. 각 영역마다 animation 주기

    거의 다 왔습니다!! 어떻게 보면 가장 재미있는 부분이 시작됩니다 :)

    바로 애니메이션인데요!! 앞서 현준님의 자료를 보셨으면 약간 감이 오실겁니다. 애니메이션을 위해 우리는 stroke-dashoffset을 targetRad 만큼 줬다가, 0.025 (초기 stroke-dashoffset) 로 다시 돌아올겁니다.

    즉, 영역의 길이만큼 왼쪽으로 댕겨놨다가, 3번에서 지정한 위치로 다시 돌아오는 겁니다. 언변이 부족해 이해가 잘 되실지 모르겠네요 ㅜㅜ 읽으시는 대부분의 분들이 개발자 분들일 것이므로, 말보단 코드가 더 나을 것 같습니다 😅

     

    css 내에서 keyframes 선언으로 애니메이션을 만들 수도 있지만, 그렇다면 값들을 동적으로 받기가 어렵습니다.

    이를 위해 svg 에서 지원하는 animate 태그를 사용합니다.

    animate 태그 내에서 여러 가지 속성을 사용해 마치 css keyframe 을 사용하는 것 처럼 사용할 수 있습니다.

     

    하나하나 설명해보자면,

    우리는 stroke-dashoffset 값을 변경시킬 것이므로 attributeName을 해당 속성으로 지정합니다.

    begin으로 언제 해당 애니메이션이 시작할지 알려줍니다. 저는 각 영역이 duration 만큼 딜레이되게 하였습니다.

    앞서 말했듯이 from, to 속성을 사용해 stroke-dashoffset을 targetRad에서 0.025만큼 이동시켜주고요.

    dur 속성을 지정해 duration 만큼 해당 애니메이션이 동작하게 합니다.

    마지막으로 fill: freeze 를 꼭 넣어줘야 합니다!! 해당 속성은 애니메이션이 끝난 마지막 색상으로 freeze 시켜줍니다. 없으면 처음 색상인 #fff로 돌아갈거예요 :(

     

    5. viewBox 개선 및 hover 시 scale up

    이해를 더 원활히 하기 위해 기존에 viewBox를 -1 -1 2 2 로 설정했는데요. 이렇게 하면 문제가 발생합니다.

    이게뭐야

    차트가 영역을 넘어가게 됩니다. 이는 viewBox 가 실제 그려진 영역보다 가까이서 보여지게 되는건데요, 아까 설정한 stroke-width 만큼 차트가 커져 영역 밖으로 벗어나게 되는것입니다!! 그림으로 그려보면 다음과 같습니다.

    즉, 차트를 원활히 보기 위해 우리는 viewBox를 확대해야 합니다. 위와 같은 속성으로 변경해주시면 문제 없이 차트가 보여지게 될 것입니다.

    마지막으로 약간의 재미를 위해 css :hover selector를 사용해 transform : scale(val); 를 추가해주시면 끝입니다!

     

    마무리

    기존에 canvas를 사용해봤기에 svg를 사용한 그래픽 요소 구현을 꼭 해보고 싶었습니다.

    처음엔 정말 이해하기 어려워서 던지고 싶었는데, 우아한테크캠프 4기의 손지호님, 최현준님 덕분에 정확한 이해와 구현에 성공한 것 같습니다. 사실 SVG를 이용하여 구현한 차트가 하나 더 남아있습니다 :) 관심 있으신 분들은 아래 링크로 이동해주세요 하하핳

     

    SVG 왕초보와 함께하는 그래프/차트 만들기 - 2. 곡선 그래프

     

    참고 자료

     

    viewBox

    viewBox란? viewBox는 svg요소가 가지는 svg의 요소가 svg요소의 viewport의 경계범위까지 확장 또는 축소, 위치지정 및 분할을 할 수 있게 해주는 속성입니다. viewBox는 4개의 값을 가지게 되는데, 이는 다

    ryujek.tistory.com

     

    A simple pie chart in SVG

    Learning the trigonometry trio — sin cos and tan — is right near the top of my reverso bucket list (things I hope to die before I have to…

    medium.com

     

    SVG와 삼각 함수로 도넛 차트 만들어보기

    이번 포스팅에서는 얼마 전에 필자가 삽질했던 내용인 SVG로 도넛 차트 그려보기에 대해서 이야기해볼까 한다. 사실 도넛 차트를 그리는 것 자체는 SVG가 제공하는 엘리먼트를 사용하면 되기 때

    evan-moon.github.io

     

    SVG Animation

    SVG Animation

    www.notion.so

     

    댓글

Designed by Tistory.