Svg로 Rounded rect형태의 progress bar 만들기 (feat. animation, path)

Svg로 Rounded rect형태의 progress bar 만들기 (feat. animation, path)

Progress bar를 구현해야한다.
웹에서 찾을 수 있는 여러 예제들을 참고하여 사용해도 되지만, 커스텀하기 어려운 경우 직접 구현을 해야한다.
바로 생각나는 엣지 케이스는 무엇일까 ?

Progress bar

Rounded progress bar의 코너 부분

채워지는 bar의 Corner 부분!

  • base bar의 코너가 rouneded가 아니거나 + 채워지는 bar도 rounded일경우 디테일한 부분까지 챙기지 않아도 되지만,
  • base bar의 코너가 rounded이면서 + 채워지는 bar가 rouned가 아닐경우, 마지막 채워지는 부분에 rounded를 대응해야한다.
채워질 때 마지막 코너 부분

생각나는 방법은 3가지가 있다.

  1. css가 가장자리에 닿을 경우의 퍼센트를 계산하여 radius값을 올리기
  2. css의 masking 사용
  3. svg의 gradient를 활용하여 fill animation으로 구현

1번과 2번의 경우 base bar와 채워지는 bar 2가지 모양을 만들어서, 채워지는 bar의 corner를 대응해야한다.
1번의 경우 부자연스러움이 걱정되었고, 2번의 경우 구현하는 방법이 상상이 되지 않았다.
3번의 경우 2가지 bar를 만드는 것이 아닌, 하나의 bar에서 채워지는 속성을 컨트롤하여 구현하기 때문에 1번 2번보다 자연스럽게 구현할 수 있다고 생각하였다.
또한 svg로 선택할 경우 그래프 이외에 커스텀한 디자인이 추가될 경우 대응이 가능하기때문에 3번 방법으로 선택하여 구현해보았다.

해당 포스팅은 3번 구현에 대한 내용을 작성하려고한다.

고려할 부분

3번으로 진행할 경우 2가지 케이스를 확인해야한다.

  1. 색상이 채워지는 형태로 구현가능한지
  2. 반응형 대응은 어떻게 해야할지

기타) 애니메이션도 챙겨보자.

1. 채워지듯이 - Svg와 그라데이션

⚠️ 이 방법을 사용하기 전에 전제조건은,
디자인이 모두 flat한 형태의 디자인일 경우에만 가능하다. 만약 채워지는 bar의 fill이 단색이 아니라 디자인이 추가되어있을 경우 사용할 수 없다.

svg는 형태를 만들고, 그 안을 fill로 채울 수 있다. prgress bar에서의 100%를 향해 채워지는 형태로 생각해보았다.

현재 svg에는 색상을 나눌 수 있는 속성은 따로 없다. 대신 그라데이션 효과를 사용하면(linear gradient) fill에 2가지 이상의 색상을 줄 수 있다. 그라데이션은 2가지 이상의 색상을 정의하고, 색상의 position을 정의하면, 각각의 위치에서 서로의 위치 사이의 색상이 점진적으로 정의된다.

다른 위치의 그라데이션

여기서 각각의 poisition을 동일하게 둘 경우, 둘 사이의 정의할 색상이 존재하지 않기 때문에 색상이 2가지 단색으로 존재할 수 있다.

같은 위치의 그라데이션

위 그림의 중앙 point를 오른쪽으로 움직이면 왼쪽에서 오른쪽으로 채워지는 형태가 될거라고 기대할 수 있다.

2. 반응형 대응

그래프가 고정형이 아닌 모바일 대응을 해야할 경우, 폭을 대응해야한다.
높이는 유지하고 폭을 대응해야하기 때문에 scale로 늘릴 수는 없다. (정비율로 커지기 때문에)
방법은 shape의 point를 옮기는 방법이 있다. 아래와 그림과 같이 가장 오른쪽의 point들을 늘리면 된다.

폭 늘리기

구현

0. 기본

bar는 rect 태그를 활용하여 구현한다. rx에는 radius의 값을 추가한다.

1
<rect x="0" y="0" width="200" height="50" rx="8" fill="none"/>

1. 채워지듯이

  • svg의 linerGradient의 stop 태그로 각 position을 정의하면된다.
  • defs에 linerGradient를 추가하고, 하위의 stop 태그 > offset에 정의하면된다.
  • 애니메이션까지 추가하고 싶다면 svg의 animate 태그를 사용하면된다.
    • 원하는 지점은 animation의 to 속성에 비율을 정의하면 된다. (0 ~ 1사이)
    • 애니메이션을 사용하지 않는다면 stop의 offset속성에 비율을 정의하면 된다.
  • linerGradient에 정의한 id를 참조할 모양의 형태에 url로 id를 참고하면 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
<svg ...>
<defs>
<linearGradient id="left-to-right">
<stop offset="0" stop-color="#007AFF">
<animate dur="2s" attributeName="offset" fill="freeze" from="0" to="1" />
</stop>
<stop offset="0" stop-color="#D8D8D8">
<animate dur="2s" attributeName="offset" fill="freeze" from="0" to="1" />
</stop>
</linearGradient>
</defs>
<rect id="Rectangle" x="0" y="0" width="200" height="50" rx="8" fill="url(#left-to-right)"/>
</svg>

2. 반응형 대응

  • bar를 구현하는 rect태그의 width 값을 다이나믹으로 대응하면 가능하다.
  • 이때에 fill은 비율로 계산되므로 width가 계산되면 자동 계산될 것이다.

최종