Frontend/CSS

CSS로 직접 그래프 만들기(bar, donut 그래프) + 애니메이션 효과 추가

둉이 2021. 8. 20. 18:44

보통 웹 개발을 할 때 그래프를 다룰 일이 생기면 라이브러리를 찾아서 사용하곤 했다.

 

이번에도 라이브러리를 이용하여 도넛 그래프 8개를 2행 4열로 배치하는 화면을 만드는 작업을 하게 됐는데, 그래프 배치가 내 맘대로 되지 않는 것이었따...!

 

동일한 크기의 반응형 사이즈로 도넛 그래프를 배치해야 하는데 반응형도 잘 안되고, 무엇보다 일렬로 그래프 배치가 안되길래 빡쳐서 내가 직접 도넛 그래프를 만들었다. (구글링 조아~~)

 

도넛 그래프를 만드니 생각보다 쉽고 할 만 하길래 막대 그래프(progress bar)도 만들었다.

 

사실 트리맵 그래프도 만들 수 있지 않을까 싶었는데 이건 그냥 jquery의 힘을 빌려서 커스텀만 하기로 했다.

나중에 좀 한가해지면 귀염 뽀짝한 트리맵 그래프를 만들어서 codepen에 업로드 해야징!

 

그래프 만들기

막대 그래프

1. width 속성 이용

 

막대 그래프의 HTML은 아래 코드처럼 막대의 바탕이 되는 부분(div) 안에 채워지는 부분(div)을 만들면 된다.

안쪽 div의 가로 길이(width)를 조절하여 값을 나타내면 된다.

<!-- HTML -->
<div class="progress-bar">           
   <div class="progress"> </div>
</div>

 

CSS는 아래처럼 초기값을 할당한다.

 

두 div의 높이는 동일하게 고정 값으로 할당하고, 채워지는 div의 width에는 나타내고자 하는 값을 퍼센트 형태로 할당한다.

동적으로 값을 할당하고 싶다면 자바스크립트에서 직접 width 값을 변경해도 된다.

<!-- CSS -->
.progress-bar {
    width: 100%;
    height: 30px;
    background-color: #dedede;
    font-weight: 600;
    font-size: .8rem;
}

.progress-bar .progress {
    width: 72%;  // 나타내고자 하는 퍼센트 값을 넣으면 됩니다.
    height: 30px;
    padding: 0;
    text-align: center;
    background-color: #4F98FF;
    color: #111;
}

 

여기까지 진행하면 아래와 같은 정적인 막대 그래프 형태의 요소를 만들 수 있다.

 

이제 여기에 움직이는 애니메이션 효과를 추가하자!

애니메이션 함수를 만드는 방법은 여러 가지가 있지만, 나는 자바스크립트의 setInterval 함수를 이용하여 애니메이션을 만들었다.

 

아래 코드처럼 setInterval 함수 내에 그래프의 width 변화값을 담당하는 증가변수(여기서는 t)를 선언하고 초기값을 0으로 할당한다.

그리고 setInterval 함수 내에서 t를 1씩 증가시키면서 그래프의 width 값에 할당하여 변화를 주면 된다.

임시변수 값이 원하는 값(여기서는 totalMinwon)과 같아지면 clearInterval 함수를 호출하여 이벤트를 종료한다.

진짜 라이브러리 느낌을 내고 싶다면 해당 애니메이션 함수를 일정 시간마다 반복하여 실행하면 더욱 생동적인 그래프를 만들 수 있다.

let t = 0
bar.style.width = 0
const barAnimation = setInterval(() => {
  bar.style.width =  t + '%'
  t++ >= totalMinwon && clearInterval(barAnimation)
}, 10)

 

setInterval 함수는 첫 번째 인자로 실행할 함수, 두 번째 인자로 반복 간격(단위: ms)을 지정할 수 있다.

두 번째 인자는 생략이 가능하고, 만약 생략한다면 초고속으로 움직이는 애니메이션을 만들 수 있다.

난 느림의 미학을 존중하므로 10 정도의 값을 줬다. (=0.01초마다 반복 실행)

 

완성작은 아래와 같다. 움짤로는 뚝뚝 끊어져서 보이지만 실제로는 스무스하게 작동한다.

프로그래스바 애니메이션

 

2. background 속성 이용

 

위에서는 width 속성을 이용하여 막대 그래프를 만들었다.

하지만 위 방법을 사용하면 막대 그래프에 두 가지 색상(위 사진 기준 하늘색과 회색)만 사용할 수 있다는 단점이 있다.

라이브러리처럼 좀 더 많은 색을 막대그래프에 표시할 순 없을까?

 

이번엔 backgroud 속성을 막대 그래프를 만들어 보자!

background에 linear-gradient를 사용하여 배경값을 지정하면 HTML으로 영역을 나눠주지 않고도 한 영역에 다른 배경색을 칠할 수 있다.

// CSS 예시
...

   background: linear-gradient(to right, #6EB4E5 0 72%, #DEDEDE 72% 100%);
...

 

또한, 1번 방법과는 다르게 HTML 요소를 하나만 사용해도 된다는 장점이 있다.

<!-- 막대 그래프 -->
<div class="progress-bar2"></div>

 

CSS는 위 1번의 .progress-bar 부분과 유사하나, width 속성 값을 100%로 주고 background 값에 linear-gradient를 사용하여 배경값을 할당하면 된다.

<!-- CSS -->
.progress-bar2 {
  width: 100%;
  height: 30px;
  background: linear-gradient(to right, #4F98FF 0 72%, #DEDEDE 72% 100%);
  font-weight: 600;
  font-size: .8rem;
}

 

여기서 주의! linear-gradient의 첫 번째 인자로 to right를 넣어야 한다.

첫 번째 방향 인자의 기본값은 to-bottom인데, to right 값을 주지 않으면 아래 사진처럼 뒤틀린 프로그래스 바가 탄생한다.

괴...괴물!

 

그리고 이제 애니메이션 함수를 만들자!

1번의 자바스크립트 코드와 유사하지만, 다른 점이 있다면 증가변수 t를 width 속성에 사용하지 않고 linear-gradient의 비율 값에 사용한다는 점이다.

 

코드를 보면서 알아보자.

let t2 = 0
const barAnimation2 = setInterval(() => {
  bar2.style.background = `linear-gradient(to right, #4F98FF 0 ${t2}%, #dedede ${t2}% 100% )`
  t2++ >= totalMinwon && clearInterval(barAnimation2)
}, 10)

 

1번의 코드와 유사하지만 그래디언트 비율 값을 조정하여 그래프의 움직임을 표현했다.

 

 

3. 값이 여러 개인 막대 그래프 표현

 

위에서 만든 막대 그래프는 아쉽게도 한가지 값만 표현이 가능하다.

하지만 두 가지 이상의 값을 막대 그래프에 표출하고 싶다면 어떻게 해야 할까?

 

2번의 linear-gradient처럼 코드를 짜되, 좀 더 복잡한 방식으로 진행하면 된다.

 

예를 들어, totalMinwon(=72), resolveMinwon(=12) 두 값을 막대그래프에 나타내고 싶다고 한다면, 이런 식으로 자바스크립트 코드를 수정하면 된다.

let t3 = 0
const barAnimation3 = setInterval(() => {
  bar3.style.background = totalMinwon > t3 ? `linear-gradient(to right, #4F98FF 0 ${t3}%, #dedede ${t3}% 100% )` : 
  totalMinwon + resolveMinwon > t3 ? `linear-gradient(to right, #4F98FF 0 ${totalMinwon}%, #f44336 ${totalMinwon}% ${t3}%, #dedede ${t3}% 100%)` : 
  `linear-gradient(to right, #4F98FF 0 ${totalMinwon}%, #f44336 ${totalMinwon}% ${totalMinwon + resolveMinwon}%, #dedede ${totalMinwon + resolveMinwon}% 100%)`
  t3++ >= 100 && clearInterval(barAnimation3)
}, 10)

 

코드가 많이 더러워서 죄송합니다... ㅜㅜ 설명해 보자면,

 

① 3번째 줄

- 첫 번째 값은 0 ~ 첫번째 값(%) 범위 내에 그려져야 한다.

- 첫 번째로 나타내고자 하는 값인 totalMinwon 값보다 증가변수(t3)의 값이 더 크다면 첫 번째 값이 그래프에 그려지고 있는 상태이다. 따라서 첫 번째 값 색상(#4F98FF)의 비율은 t3와 같다.

- 아직 두 번째 값이 나타나야 할 단계가 아니므로 두 번째 값 색상(#F44336)은 사용하지 않는다.

 

② 4번째 줄

- 첫 번째 값인 totalMinwon의 값보다 증가변수(t3)의 값이 커지면 4번째 줄의 코드가 실행된다.

- 두 번째 값은 첫 번째 값(%) ~ 첫 번째 값 + 두 번째 값(%) 범위 내에 그려져야 한다. 따라서 첫 번째 값 + 두 번째 값(totalMinwon + resolveMinwon)보다 증가변수(t3)의 값이 커지기 전까지 첫 번째 값 ~ 증가변수(t3) 만큼의 비율에 두 번째 값 색상(#F44336)을 칠해주면 된다.

 

③ 5번째 줄

- 증가변수(t3)의 값이 두 번째 값의 범위를 초과하는 경우 5번째 줄의 코드가 실행된다.

- 이제 첫 번째 값과 두 번째 값 범위 색이 다 칠해졌으므로 나머지 범위인 첫 번째 값 + 두 번째 값(%) ~ 100 만큼의 공간을 빈 공간 색상(#DEDEDE)로 채워준다.

 

 

완성된 그래프는 이러하다. 근데 왜 티스토리에 업로드하면 gif가 뚜두둑 끊겨서 보일까? ㅠㅠ

막대 그래프 2

 

도넛 그래프

도넛 그래프는 만들기 조금 복잡하다...

특히, 나는 도넛 정 가운데에 글씨도 쓸 수 있게끔 구현해야 해서 pseudo element를 떡발라서 만들었다.

이거 만들면서 pseudo 빡숙에 좀 더 가까워진 것 같아서 기부니가 좋다.

 

먼저, HTML은 별 거 없다. 그냥 도넛 모양을 그려줄 영역 1개만 만들어 준다.

data-percent 속성은 나중에 before 선택자에서 사용할 것이다.

HTML 코드에서 저렇게 넣어주지 않고 자바스크립트를 이용하여 값을 할당해도 된다.

<!-- data-percent 안에 퍼센트 값을 준다. -->
<div class="donut" data-percent="85.4"></div>

 

1. 값이 하나인 도넛 그래프

 

CSS는 다음과 같다. 막대 그래프를 만들 때는 linear-gradient를 사용했지만 도넛 그래프를 만들 때는 conic-gradient를 사용하여 배경색을 칠해주면 된다.

.donut {
    width: calc(100% - 16px);
    padding-bottom: calc(100% - 16px);
    margin: 0 auto;
    border-radius: 50%;
    position: relative;
    text-align: center;
    transition: background .3s ease-in-out;
    background: conic-gradient(#3F8BC9 0% 72%, #F2F2F2 72% 100%);
}

.donut::before {
    color: #fff;
    width: 70%;
    padding: calc(35% - .64vw) 0;
    background: #264057;
    border-radius: 50%;
    position: absolute;
    left: 15%;
    top: 15%;
    display: block;
    content: attr(data-percent)'%';
    transform: skew(-0.03deg);
    margin: auto;
    font-size: 1.1vw;
    font-size: 2vw;
    padding: calc(35% - 1.3vw) 0;
}

 

정적인 도넛 그래프가 필요하면 위 CSS 코드처럼 conic-gradient 내부에 상수 퍼센트 값을 넣으면 된다.

하지만 저런 식으로 그래프를 만들면 나중에 다른 퍼센트 값으로 그래프를 그리기가 애매해진다.

 

이럴 때는 퍼센트 값을 변수로 할당하면 된다.

 

보통 CSS 내에서 변수를 사용하는 방법은 ① HTML에서 dataset 변수를 선언하여 가져와 사용하는 방법② CSS 내의 :root 가상 선택자 내에서 변수를 선언하여 사용하는 방법이 있다.

 

아쉽게도 1번 방법으로 변수값을 가져오니 CSS 내에서 퍼센트 값으로 인식을 못한다 ㅠㅠㅠ

(혹시 dataset 변수를 퍼센트 값으로 CSS에서 쓰는 법 아시는 분은 알려주시면 감사드리겠습니다..!)

따라서 2번 방법으로 진행하거나 자바스크립트 내에서 style에 접근하여 background 값 자체를 바꿔주는 방식으로 사용하면 된다.

 

2번 방법에 대한 설명은 아래 링크 참조!

 

CSS - HTML 태그 속성을 CSS에서 사용 / CSS 변수 선언 및 사용, 수정, 가져오기

태그의 어트리뷰트 값을 받아와서 CSS에서 사용할 수 있다. 1. HTML CSS에서 사용할 어트리뷰트 값을 태그에 지정한다. 어트리뷰트 이름은 아무렇게나 지어도 되지만, 보통은 data-* 형태로 명명하는

guiyomi.tistory.com

 

나는 어차피 애니메이션도 만들어서 넣어줄 것이므로 자바스크립트에서 값을 바꿔주는 방법으로 진행했다.

 

그냥 간단히 이런 식으로 값을 바꿔주면 도넛 그래프가 완성된다.

const donut = document.querySelector(".donut")
donut.dataset.percent = totalMinwon
donut.style.background = `conic-gradient(#3F8BC9 0% ${totalMinwon}%, #F2F2F2 ${totalMinwon}% 100%)`

 

완성된 그래프는 다음과 같다.

 

 

이제 애니메이션을 넣어보자!

사실, 위에서 막대그래프를 만들 때 썼던 자바스크립트 코드를 그대로 쓰면 된다.

더 자세히 말하자면 위에서 쓴 코드에서 linear-gradient를 conic-gradient로 고치고, to right 파라미터를 빼면 된다.

let t4 = 0
const donutAnimation = setInterval(() => {
  donut.dataset.percent = t4
  donut.style.background = `conic-gradient(#4F98FF 0 ${t4}%, #DEDEDE ${t4}% 100% )`
  t4++ >= totalMinwon && clearInterval(donutAnimation)
}, 10)

 

 

2. 값이 여러 개인 도넛 그래프

 

이것도 1번 도넛 그래프처럼 위에서 썼던 코드에서 linear-gradient를 conic-gradient로 고치고, to right 파라미터만 빼면 동작한다.

let t5 = 0
const donutAnimation2 = setInterval(() => {
  donut2.dataset.percent = t5
  donut2.style.background = totalMinwon > t5 ? `conic-gradient(#4F98FF 0 ${t3}%, #dedede ${t3}% 100% )` : 
  totalMinwon + resolveMinwon > t5 ? `conic-gradient(#4F98FF 0 ${totalMinwon}%, #f44336 ${totalMinwon}% ${t3}%, #dedede ${t3}% 100%)` : 
  `conic-gradient(#4F98FF 0 ${totalMinwon}%, #f44336 ${totalMinwon}% ${totalMinwon + resolveMinwon}%, #dedede ${totalMinwon + resolveMinwon}% 100%)`
  t5++ >= totalMinwon + resolveMinwon && clearInterval(donutAnimation2)
}, 10)

 

 

완성작은 아래와 같다!

도넛 그래프 만드니까 든킨드나쓰 마렵다

 

cf) jquery의 animate를 사용한 애니메이션 생성

jquery의 animate를 사용하면 코드 한 줄로 애니메이션 효과를 만들 수 있다.

animate를 응용하면 부드러운 크기 변경이나 스크롤도 만들 수 있으니 꼭 기억해 두자!

$('').animate({변경하고자 하는 속성명 : 변경할 값}, ms)

// 예시
// $(".progress-bar .progress").animate({width: totalMinwon + '%'}, 1000)

 

아래는 코드펜으로 실행한 결과이다!

See the Pen progress bar animation by MiJeong Kim (@sap03110) on CodePen.