스킬바 UI
스크롤 위치에 따라 막대형, 원형 스킬바를 애니메이션하는 방법을 학습한다.
코드 블록의 Try it Yourself 버튼으로 직접 실행할 수 있다.
구문
스킬바 UI
스킬바는 포트폴리오 사이트에서 기술 숙련도를 시각적으로 보여주는 UI 요소이다. 스크롤 위치에 따라 막대나 원이 채워지는 애니메이션을 구현한다.
1. 막대형 스킬바
스크롤이 특정 위치에 도달하면 막대가 늘어나면서 퍼센트 숫자가 올라가는 애니메이션이다.
jQuery 3 기반의 막대형 스킬바를 만들어줘. 조건은 다음과 같아. 1) 프로그레스 바 3개를 세로로 배치하고, 각각 목표 퍼센트를 data-rate 속성으로 지정한다(30%, 90%, 60%). 2) 스크롤이 애니메이션 영역에 도달하면 막대의 width가 0%에서 목표 퍼센트까지 2500ms 동안 늘어난다. 3) 동시에 퍼센트 숫자가 0부터 목표값까지 카운트업된다. 4) 애니메이션은 1회만 실행된다. 5) 이벤트 바인딩은 .on('scroll')을 사용한다.
<div class="content">contents</div>
<div class="animation">
<div class="progress-bar">
<div class="bar"></div>
<div class="rate" data-rate="30"></div>
</div>
<div class="progress-bar">
<div class="bar"></div>
<div class="rate" data-rate="90"></div>
</div>
<div class="progress-bar">
<div class="bar"></div>
<div class="rate" data-rate="60"></div>
</div>
</div>
<div class="content">contents</div>
| 줄 | 설명 |
|---|---|
| 1 | 스크롤 확인용 콘텐츠 영역이다 |
| 2 | 애니메이션 영역의 시작이다. 이 요소의 위치를 기준으로 스크롤을 감지한다 |
| 3 | 하나의 프로그레스 바 묶음이다 |
| 4 | 실제로 늘어나는 색상 막대이다 |
| 5 | 퍼센트 숫자를 표시하는 영역이다. data-rate에 목표 퍼센트 값을 지정한다 |
1.1. 원형 스킬바
SVG의 circle 요소와 stroke-dashoffset 속성을 활용한 원형 프로그레스 바이다.
jQuery 3 기반의 원형 스킬바를 만들어줘. 조건은 다음과 같아. 1) SVG circle 요소(반지름 100, 둘레 628)를 사용한 원형 프로그레스 바 4개를 가로로 배치한다. 2) 각 원의 중앙에 퍼센트 숫자를 표시하고, data-num 속성으로 목표값을 지정한다(20, 60, 80, 50). 3) stroke-dasharray와 stroke-dashoffset을 628로 설정하고, 스크롤 시 dashoffset을 줄여서 선이 그려지는 애니메이션을 구현한다. 4) 12시 방향부터 시작하도록 rotate(-90deg)를 적용한다. 5) 각 원마다 다른 stroke 색상을 지정한다. 6) hasClass로 중복 실행을 방지한다. 7) 이벤트 바인딩은 .on('scroll')을 사용한다.
<section>
<h2>content</h2>
</section>
<div class="charts">
<div class="chart">
<h2 data-num="20">0</h2>
<svg>
<circle cx="110" cy="110" r="100"></circle>
</svg>
</div>
<div class="chart">
<h2 data-num="60">0</h2>
<svg>
<circle cx="110" cy="110" r="100"></circle>
</svg>
</div>
<div class="chart">
<h2 data-num="80">0</h2>
<svg>
<circle cx="110" cy="110" r="100"></circle>
</svg>
</div>
<div class="chart">
<h2 data-num="50">0</h2>
<svg>
<circle cx="110" cy="110" r="100"></circle>
</svg>
</div>
</div>
<section>
<h2>content</h2>
</section>
| 줄 | 설명 |
|---|---|
| 1~3 | 스크롤 공간 확보용 섹션이다 |
| 4 | .charts — 원형 차트들을 감싸는 컨테이너이다 |
| 6 | data-num="20" — 목표 퍼센트 값이다. 초기 텍스트는 0이다 |
| 8 | circle — SVG 원 요소이다. cx, cy는 중심 좌표, r은 반지름이다 |
1.2. SVG 라인형 스킬바
circle 대신 line 요소를 사용한 직선형 변형이다.
jQuery 3 기반의 SVG 라인형 스킬바를 만들어줘. 조건은 다음과 같아. 1) SVG line 요소(x1=0, y1=0, x2=500, y2=0)를 사용한 직선형 프로그레스 바 3개를 가로로 배치한다. 2) stroke-dasharray와 stroke-dashoffset을 300으로 설정하고, 스크롤 시 dashoffset을 줄여서 선이 그려지는 애니메이션을 구현한다. 3) 각 라인 중앙에 퍼센트 숫자 카운트업을 표시한다. 4) 각 라인마다 다른 stroke 색상을 지정한다. 5) hasClass로 중복 실행을 방지한다. 6) $(function(){})로 감싸고, 이벤트 바인딩은 .on('scroll')을 사용한다.
<section>
<h2>content</h2>
</section>
<div class="charts">
<div class="chart">
<h2 data-num="20">0</h2>
<svg>
<line x1="0" y1="0" x2="500" y2="0"></line>
</svg>
</div>
<div class="chart">
<h2 data-num="20">0</h2>
<svg>
<line x1="0" y1="0" x2="500" y2="0"></line>
</svg>
</div>
<div class="chart">
<h2 data-num="80">0</h2>
<svg>
<line x1="0" y1="0" x2="500" y2="0"></line>
</svg>
</div>
</div>
<section>
<h2>content</h2>
</section>
| 줄 | 설명 |
|---|---|
| 8 | line 요소는 시작점(x1, y1)에서 끝점(x2, y2)까지 직선을 그린다 |
| 6 | data-num — 목표 퍼센트 값이다. circle 예제와 동일한 방식이다 |
원본 코드에서 라인형(1.2)의 JS는 $(function(){})로 감싸져 있지 않았다. 문서 로드 전에 DOM 요소를 찾으면 undefined 오류가 발생할 수 있으므로 반드시 감싸야 한다.
2. GSAP으로 원형 스킬바 만들기
GSAP과 ScrollTrigger를 사용하면 jQuery 없이도 스킬바 애니메이션을 구현할 수 있다.
GSAP과 ScrollTrigger로 원형 스킬바를 만들어줘. 조건은 다음과 같아. 1) CSS conic-gradient와 CSS 변수 --p를 사용하여 원형 게이지를 구현한다. 2) gsap.to로 CSS 변수 --p를 0에서 목표 퍼센트까지 4초 동안 변경하여 게이지를 채운다. 3) ease는 expo.out을 사용한다. 4) 원 중앙의 숫자도 0부터 목표값까지 카운트업하되, modifiers 플러그인의 toFixed()로 소수점을 제거한다. 5) ScrollTrigger의 toggleActions를 play pause resume reset으로 설정하여 스크롤 위치에 따라 애니메이션을 제어한다. 6) timeline을 사용하여 게이지 채우기와 숫자 카운트를 동시에 실행한다.
2.1. CDN 로드
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js"></script>
| 줄 | 설명 |
|---|---|
| 1 | GSAP 핵심 라이브러리를 불러온다 |
| 2 | ScrollTrigger 플러그인을 불러온다. 스크롤 위치 기반 애니메이션에 필요하다 |
2.2. HTML 구조
<div class="circular-pbar">
<span class="circular-pbar-counter">0</span>
</div>
| 줄 | 설명 |
|---|---|
| 1 | 원형 프로그레스 바의 컨테이너이다. CSS의 conic-gradient로 원형 게이지를 만든다 |
| 2 | 중앙에 표시할 숫자 영역이다. 초기값은 0이다 |
2.3. 기본 CSS
.circular-pbar {
width: 200px;
height: 200px;
border-radius: 50%;
background: conic-gradient(darkred 33%, 0, black);
position: relative;
}
.circular-pbar-counter {
position: absolute;
font-size: 3em;
color: white;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
| 줄 | 설명 |
|---|---|
| 3 | border-radius: 50%로 정사각형을 원으로 만든다 |
| 5 | conic-gradient — 원뿔형 그라디언트이다. darkred가 33% 지점까지 채워지고 나머지는 black이다 |
| 8~14 | 숫자를 absolute + translate로 원의 정중앙에 배치한다 |
2.4. CSS 변수로 애니메이션 준비
.circular-pbar에 CSS 지역변수 --p를 선언하고 0을 할당한다. 배경의 색상 위치에 이 변수를 넣으면 JS에서 변수 값만 바꿔도 게이지가 움직인다.
.circular-pbar {
--p: 0;
background: conic-gradient(#df3030 var(--p, 0), 0, #cacaca);
}
| 줄 | 설명 |
|---|---|
| 2 | --p: 0 — CSS 사용자 정의 속성을 선언하고 초기값 0을 넣는다 |
| 3 | var(--p, 0) — --p 값을 그라디언트의 색상 위치로 사용한다. GSAP이 이 값을 변경하면 게이지가 채워진다 |
2.5. GSAP으로 게이지 애니메이션
gsap.to를 사용하여 CSS 변수 --p의 값을 변경하면 원형 게이지가 채워진다.
gsap.to('.circular-pbar', {
'--p': '33%',
duration: 4,
ease: 'expo.out',
});
| 줄 | 설명 |
|---|---|
| 1 | gsap.to — 대상 요소를 지정한 속성 값까지 애니메이션한다 |
| 2 | '--p': '33%' — CSS 변수 --p를 0에서 33%까지 변경한다 |
| 3 | duration: 4 — 4초 동안 진행한다 |
| 4 | ease: 'expo.out' — 처음에 빠르고 끝에서 느려지는 가속 곡선이다 |
2.6. 숫자 카운트 + ScrollTrigger 적용
스크롤 위치에 따라 숫자도 함께 올라가도록 확장한다.
const circles = document.querySelectorAll('.circular-pbar');
circles.forEach((el) => {
const counter = el.querySelector('.circular-pbar-counter');
const tg = counter.textContent + '%';
const tm = gsap.timeline({
defaults: { duration: 4, ease: 'expo.out' },
scrollTrigger: {
trigger: el,
toggleActions: 'play pause resume reset',
},
});
tm.from(counter, {
textContent: 0,
modifiers: {
textContent: (textContent) => {
return textContent.toFixed();
},
},
});
tm.to(el, { '--p': tg }, 0);
});
| 줄 | 설명 |
|---|---|
| 1 | 모든 .circular-pbar 요소를 선택한다 |
| 3 | forEach로 각 원형 바를 순회한다 |
| 5 | counter 요소의 텍스트(목표 퍼센트 값)를 읽어 '%'와 결합한다. 이 값은 CSS 변수로 사용된다 |
| 7~8 | GSAP timeline을 생성하고 기본값으로 지속 시간 4초, 가속 곡선 expo.out을 할당한다 |
| 9~12 | ScrollTrigger를 연결한다. toggleActions는 순서대로 진입/이탈/재진입/재이탈 시 동작이다 |
| 15~22 | tm.from — textContent를 0부터 목표값까지 애니메이션한다 |
| 17~20 | modifiers — 속성 변경값을 실시간으로 가공하는 GSAP 내장 플러그인이다. .toFixed()로 소수점을 제거한다 |
| 24 | tm.to — CSS 변수 --p를 목표값까지 변경한다. 세 번째 인자 0은 timeline 시작과 동시에 실행한다는 의미이다 |