원형 프로그레스 스크롤 애니메이션
Overview
700vh 높이의 섹션에서 20개 이미지가 중심에서 방사형으로 산란되고, SVG 원형 프로그레스가 스크롤에 연동되는 인터랙티브 프로젝트입니다. translate3d 기반 3D 깊이감과 z축 블러 계산으로 몰입감 있는 시각 효과를 구현했습니다.
14개의 타이포 텍스트가 깊이별로 부유하며, 5개 컬러 도트가 각기 다른 속도로 궤도를 회전합니다. 모바일에서는 perspective를 제거하고 2D 트랜스폼으로 전환하여 GPU 연산을 최소화하는 적응형 렌더링 전략을 적용했습니다.
Anatomy
UI 해부도
700vh 섹션은 6개의 독립 레이어로 구성됩니다. 중앙의 SVG 프로그레스 링을 중심으로 이미지와 텍스트가 방사형으로 산란되며, 궤도 도트가 개별 속도로 회전합니다.
- Scatter Images
- 20개 이미지 · z축 기반 3D 산란
- Scatter Texts
- 14개 타이포 텍스트 · 깊이 기반 blur
- Progress Circle
- SVG 원형 진행률 · 그라디언트 스트로크
- Counter
- 0→100 숫자 카운터
- Orbit Dots
- 5개 컬러 도트 · 개별 속도 회전
- BgOrbs
- circleProgress 프리셋 배경
Structure
프로젝트 아키텍처
SectionCircle은 하나의 섹션 컴포넌트 안에 scatter · progress · effects 세 영역의 요소가 응집되어 있습니다. 모바일 분기 유틸이 3D/2D 렌더링 전략을 결정합니다.
- SectionCircle.tsx
- 700vh 섹션 · 이미지 scatter · 원형 프로그레스
- scatter-img ×20
- Next/Image · 랜덤 사이즈 · 3D 포지셔닝
- scatter-text ×14
- 타이포 텍스트 · z축 깊이 blur
- SVG Progress Circle
- strokeDasharray 기반 원형 게이지
- Orbit Dots ×5
- 개별 속도·색상·크기 회전 도트
- BgOrbs.jsx
- circleProgress 프리셋
- isMobileDevice.js
- 모바일 분기 · 2D 전환
Data Flow
컴포넌트 데이터 플로우
데이터는 단방향으로 흐릅니다. 초기화 시 산란 좌표를 계산하고, ScrollTrigger가 스크롤 진행률에 따라 scatter와 progress를 동시에 업데이트합니다.
ANIMATION OUTPUT
- Images
- 3D 산란
- center → 방사형 scatter (방향별)
- Texts
- 깊이 부유
- z축 깊이 기반 blur + opacity
- Progress
- 원형 게이지
- strokeDashoffset animation
- Counter
- 숫자 카운트
- 0 → 100 텍스트 업데이트
Timeline
애니메이션 타임라인
스크롤 진행률 0%→100% 구간에서 6개의 애니메이션 레이어가 동시에 진행됩니다. ScrollTrigger의 scrub 모드로 스크롤 위치에 정밀하게 연동되며, 85% 지점에서 프로그레스 UI가 페이드아웃됩니다.
- 20개 이미지 center→방사형 산란 (개별 딜레이)
- 14개 텍스트 z축 깊이→표면 부유
- SVG strokeDashoffset 0→100%
- 5개 도트 개별 속도 회전 (0.4~2.5×)
- 숫자 0→100 카운트업
- 프로그레스 UI 전체 페이드아웃
Interaction
인터랙션 플로우
스크롤 입력은 Image Scatter와 Progress UI 두 갈래로 분기됩니다. 각 경로는 독립적으로 업데이트되며, 85% 임계값에서 프로그레스 UI의 페이드아웃 여부가 결정됩니다.
- 1
calculatePositions로 산란 좌표 계산
scatterDirections[i] × multiplier - 2
이미지별 개별 딜레이 적용 (stagger)
- 3
3D/2D 트랜스폼 분기 적용
translate3d(x, y, z) - 4
z값 기반 blur + opacity 계산
- 1
circumference 계산 (2 × PI × r)
circumference = 2 * PI * 210 - 2
quickSetter로 dashoffset 실시간 업데이트
- 3
카운터 텍스트 0→100 업데이트
- 4
5개 도트 cos/sin 궤도 회전
cx = cos(rad) × r, cy = sin(rad) × r
Patterns
핵심 기술 패턴
단순히 모션이 예쁜 것이 아니라, 매 프레임 34개 요소를 효율적으로 업데이트하는 구조로 설계했습니다. 직접 스타일 조작 · quickSetter · 모바일 2D 전환까지 — 실무에서 바로 적용할 수 있는 성능 최적화 패턴입니다.
- 3D 이미지 산란
- translate3d로 z축 -1000→2500 범위에서 이미지를 중심→방사형으로 산란 — 깊이감 있는 폭발 효과
- transform: translate3d(x, y, z) scale(s)
- 원형 프로그레스
- strokeDasharray + strokeDashoffset로 원형 게이지 구현 — quickSetter로 매 프레임 효율적 업데이트
- quickSetter(circle, 'strokeDashoffset') offset = circumference * (1 - p)
- 궤도 회전 도트
- 5개 도트가 각각 다른 속도(0.4~2.5×)로 원형 궤도 위를 회전 — cos/sin으로 좌표 계산
- cx = center + cos(rad) * radius cy = center + sin(rad) * radius
- 깊이 기반 블러
- z값으로 blur(0~10px)와 opacity(0.05~1)를 계산하여 먼 오브젝트가 흐릿하게 보이는 원근감 연출
- blur = (|z| / 1200) * maxBlur opacity = clamp(0.05, 1, 1 - t * 0.9)
- 모바일 2D 전환
- 모바일에서 perspective/preserve-3d 제거 → translate 2D로 전환하여 GPU 연산 최소화 + 홀수 이미지 숨김
- setScatterStyle2D(el, x, y, scale, opacity)
- 직접 스타일 조작
- gsap.set 오버헤드를 제거하고 el.style.transform을 직접 조작하여 20+14개 요소의 매 프레임 성능 최적화
- el.style.transform = `translate3d(...)` el.style.opacity = opacity
Responsive
반응형 전략
데스크탑 퍼스트로 설계하고 max-xl · max-md 2단계 브레이크포인트만 사용합니다. 모바일에서는 perspective를 제거하고 2D 트랜스폼으로 전환하여 GPU 부하를 최소화합니다.
- 700vh 3D 이미지 산란
- 20개 이미지 + 14개 텍스트
- perspective + preserve-3d
- 깊이 기반 blur 필터
- SVG 프로그레스 r=210
- 이미지·텍스트 수 유지
- 3D 효과 유지
- 프로그레스 크기 유지
- 뷰포트 비율 자동 조정
- scatter 방향 유지
- 이미지 10개로 절반 숨김
- 텍스트 요소 전체 숨김
- 2D 트랜스폼 전환
- scatter 배수 2.5× 확대
- 프로그레스 r=120 축소