Next.js · GSAP · SVG Progress · 3D Scatter

원형 프로그레스 스크롤 애니메이션


Overview

700vh 높이의 섹션에서 20개 이미지가 중심에서 방사형으로 산란되고, SVG 원형 프로그레스가 스크롤에 연동되는 인터랙티브 프로젝트입니다. translate3d 기반 3D 깊이감과 z축 블러 계산으로 몰입감 있는 시각 효과를 구현했습니다.

14개의 타이포 텍스트가 깊이별로 부유하며, 5개 컬러 도트가 각기 다른 속도로 궤도를 회전합니다. 모바일에서는 perspective를 제거하고 2D 트랜스폼으로 전환하여 GPU 연산을 최소화하는 적응형 렌더링 전략을 적용했습니다.

Anatomy

UI 해부도

700vh 섹션은 6개의 독립 레이어로 구성됩니다. 중앙의 SVG 프로그레스 링을 중심으로 이미지와 텍스트가 방사형으로 산란되며, 궤도 도트가 개별 속도로 회전합니다.

FORMDEPTHSPACEMOTION
75PROGRESS
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 렌더링 전략을 결정합니다.

components/SCROLL
section/1
SectionCircle.tsx
700vh 섹션 · 이미지 scatter · 원형 프로그레스
scatter/2
scatter-img ×20
Next/Image · 랜덤 사이즈 · 3D 포지셔닝
scatter-text ×14
타이포 텍스트 · z축 깊이 blur
progress/2
SVG Progress Circle
strokeDasharray 기반 원형 게이지
Orbit Dots ×5
개별 속도·색상·크기 회전 도트
effects/2
BgOrbs.jsx
circleProgress 프리셋
isMobileDevice.js
모바일 분기 · 2D 전환

Data Flow

컴포넌트 데이터 플로우

데이터는 단방향으로 흐릅니다. 초기화 시 산란 좌표를 계산하고, ScrollTrigger가 스크롤 진행률에 따라 scatter와 progress를 동시에 업데이트합니다.

INIT
calculatePositionsstart: center z:-1000end: scatter + z:2500
SCROLL
ScrollTrigger scrubupdateScatter()updateProgress()
RENDER
translate3d (desktop)translate 2D (mobile)depth blur/opacity

ANIMATION OUTPUT

Images
3D 산란
center → 방사형 scatter (방향별)
Texts
깊이 부유
z축 깊이 기반 blur + opacity
Progress
원형 게이지
strokeDashoffset animation
Counter
숫자 카운트
0 → 100 텍스트 업데이트

Timeline

애니메이션 타임라인

스크롤 진행률 0%→100% 구간에서 6개의 애니메이션 레이어가 동시에 진행됩니다. ScrollTrigger의 scrub 모드로 스크롤 위치에 정밀하게 연동되며, 85% 지점에서 프로그레스 UI가 페이드아웃됩니다.

0%20%40%60%80%100%
Image Scatter
Text Scatter
Progress Arc
Orbit Rotation
Counter
Fade Out
20개 이미지 center→방사형 산란 (개별 딜레이)
14개 텍스트 z축 깊이→표면 부유
SVG strokeDashoffset 0→100%
5개 도트 개별 속도 회전 (0.4~2.5×)
숫자 0→100 카운트업
프로그레스 UI 전체 페이드아웃

Interaction

인터랙션 플로우

스크롤 입력은 Image Scatter와 Progress UI 두 갈래로 분기됩니다. 각 경로는 독립적으로 업데이트되며, 85% 임계값에서 프로그레스 UI의 페이드아웃 여부가 결정됩니다.

SCROLL START
Image Scatter3D
  1. 1

    calculatePositions로 산란 좌표 계산

    scatterDirections[i] × multiplier
  2. 2

    이미지별 개별 딜레이 적용 (stagger)

  3. 3

    3D/2D 트랜스폼 분기 적용

    translate3d(x, y, z)
  4. 4

    z값 기반 blur + opacity 계산

Progress UISVG
  1. 1

    circumference 계산 (2 × PI × r)

    circumference = 2 * PI * 210
  2. 2

    quickSetter로 dashoffset 실시간 업데이트

  3. 3

    카운터 텍스트 0→100 업데이트

  4. 4

    5개 도트 cos/sin 궤도 회전

    cx = cos(rad) × r, cy = sin(rad) × r
progress > 0.85 ?
페이드아웃 실행프로그레스 UI 전체 opacity → 0
100% 표시 유지프로그레스 UI 100% 상태로 표시

Patterns

핵심 기술 패턴

단순히 모션이 예쁜 것이 아니라, 매 프레임 34개 요소를 효율적으로 업데이트하는 구조로 설계했습니다. 직접 스타일 조작 · quickSetter · 모바일 2D 전환까지 — 실무에서 바로 적용할 수 있는 성능 최적화 패턴입니다.

3D Scatter
3D 이미지 산란
translate3d로 z축 -1000→2500 범위에서 이미지를 중심→방사형으로 산란 — 깊이감 있는 폭발 효과
transform: translate3d(x, y, z) scale(s)
SVG Progress
원형 프로그레스
strokeDasharray + strokeDashoffset로 원형 게이지 구현 — quickSetter로 매 프레임 효율적 업데이트
quickSetter(circle, 'strokeDashoffset') offset = circumference * (1 - p)
Orbit Dots
궤도 회전 도트
5개 도트가 각각 다른 속도(0.4~2.5×)로 원형 궤도 위를 회전 — cos/sin으로 좌표 계산
cx = center + cos(rad) * radius cy = center + sin(rad) * radius
Depth Blur
깊이 기반 블러
z값으로 blur(0~10px)와 opacity(0.05~1)를 계산하여 먼 오브젝트가 흐릿하게 보이는 원근감 연출
blur = (|z| / 1200) * maxBlur opacity = clamp(0.05, 1, 1 - t * 0.9)
Mobile 2D
모바일 2D 전환
모바일에서 perspective/preserve-3d 제거 → translate 2D로 전환하여 GPU 연산 최소화 + 홀수 이미지 숨김
setScatterStyle2D(el, x, y, scale, opacity)
Direct Style
직접 스타일 조작
gsap.set 오버헤드를 제거하고 el.style.transform을 직접 조작하여 20+14개 요소의 매 프레임 성능 최적화
el.style.transform = `translate3d(...)` el.style.opacity = opacity

Responsive

반응형 전략

데스크탑 퍼스트로 설계하고 max-xl · max-md 2단계 브레이크포인트만 사용합니다. 모바일에서는 perspective를 제거하고 2D 트랜스폼으로 전환하여 GPU 부하를 최소화합니다.

Desktop1280px+
75
  • 700vh 3D 이미지 산란
  • 20개 이미지 + 14개 텍스트
  • perspective + preserve-3d
  • 깊이 기반 blur 필터
  • SVG 프로그레스 r=210
Tabletmax-xl~1280px
75
  • 이미지·텍스트 수 유지
  • 3D 효과 유지
  • 프로그레스 크기 유지
  • 뷰포트 비율 자동 조정
  • scatter 방향 유지
Mobilemax-md~768px
75
  • 이미지 10개로 절반 숨김
  • 텍스트 요소 전체 숨김
  • 2D 트랜스폼 전환
  • scatter 배수 2.5× 확대
  • 프로그레스 r=120 축소