스크롤 시 흩어지는 3D 텍스트
Overview
300vh 높이의 sticky 섹션에서 3D perspective 텍스트 비행 애니메이션을 구현한 프로젝트입니다. SplitText로 분리된 27개의 글자·단어가 z축 550~1350 깊이에서 화면으로 수렴한 뒤, 스크롤 퇴장 시 다시 발산하는 3단계 플라이 패턴을 구현했습니다.
heading은 chars로, body는 words로 이중 분할하여 서로 다른 밀도의 3D 효과를 적용하고, Math.random() 기반 스태거 순서로 각 요소의 등장 타이밍을 자연스럽게 분산합니다. 좌측의 그라디언트 Orb 구성과 함께 시각적 깊이감을 극대화한 스크롤 섹션입니다.
Anatomy
UI 해부도
SectionScatter는 sticky 컨테이너 안에서 좌측 Orb 구성과 우측 3D 플라이 텍스트가 결합된 2단 레이아웃입니다. 27개의 글자·단어가 z축 깊이에서 화면으로 수렴하는 perspective 애니메이션이 핵심입니다.
- Main Orb
- 그라디언트 구체 · 스크롤 회전
- Small Orb
- 핑크 구체 · 궤도 회전
- Year Text
- 2026 배경 텍스트 · x 드리프트
- Heading Chars
- SplitText chars · 3D fly-in/out
- Body Words
- SplitText words · 3D fly-in/out
- Info Grid
- Role · Tools · E-Mail · Designer
Structure
프로젝트 아키텍처
SectionScatter는 하나의 컴포넌트 안에서 SplitText · ScrollTrigger · 랜덤 데이터를 조합하여 3D 플라이 애니메이션을 구현합니다. 27개 요소의 랜덤 값은 컴포넌트 상단 상수로 분리되어 있습니다.
- SectionScatter.tsx
- 300vh 섹션 · 3D fly 애니메이션 · ScrollTrigger
- SplitText (GSAP)
- heading → chars, body → words 분할
- BgOrbs.jsx
- scatterText 프리셋 배경 블롭
- staggerOrder[]
- 27개 랜덤 순서값 (0~1)
- wordRotateX[]
- 27개 랜덤 rotateX (-80°~80°)
- wordTranslateZ[]
- 27개 랜덤 z 오프셋 (550~1350)
Data Flow
컴포넌트 데이터 플로우
데이터는 3단계로 흐릅니다. SplitText로 텍스트를 분할하고, 초기 3D 상태를 설정한 뒤, ScrollTrigger가 스크롤 progress에 따라 fly-in · rest · fly-out을 제어합니다.
ANIMATION OUTPUT
- chars
- 3D 플라이인
- z → 0, rotateX → 0, stagger
- words
- 3D 플라이인
- z → 0, rotateX → 0, stagger
- mainOrb
- 회전
- rotation: progress × 30°
- smallOrb
- 궤도 회전
- rotation: progress × 120°, origin 250%
Timeline
애니메이션 타임라인
스크롤 진행률 0%~100% 구간에서 5개의 애니메이션 레이어가 동시에 진행됩니다. 텍스트는 fly-in · rest · fly-out 3단계로 분리되고, Orb와 Year는 전 구간에서 지속 회전합니다.
- chars + words z:1350→0 순차 수렴 (stagger 랜덤)
- z:0에서 정지 — 텍스트 읽기 구간
- chars + words z:0→1350 순차 발산 (stagger 랜덤)
- main 30° + small 120° 회전
- x: (progress-0.5) × 300 좌우 드리프트
Interaction
인터랙션 플로우
스크롤 progress 값에 따라 3개의 페이즈로 분기됩니다. 각 페이즈는 임계값(flyInEnd, flyOutStart)을 기준으로 전환되며, 27개 요소의 랜덤 스태거 순서가 자연스러운 등장·퇴장 타이밍을 만듭니다.
- 1
staggerOrder로 요소별 딜레이 계산
delay = order × 0.6 - 2
z값 보간: translateZ → 0
z: interpolate(tz, 0, eased) - 3
깊이 기반 opacity 계산
opacity: 1 - z / 350
- 1
모든 요소 z:0 고정
translateZ: 0 - 2
opacity:1, rotateX:0
텍스트 읽기 구간 - 3
Orb 회전만 지속 진행
- 1
역방향 스태거 순서 적용
reverse stagger order - 2
z값 복귀: 0 → 원래 translateZ
z: interpolate(0, tz, eased) - 3
opacity 페이드아웃
opacity → 0
Patterns
핵심 기술 패턴
단순한 스크롤 애니메이션이 아니라, 3D perspective와 랜덤 스태거를 조합하여 27개 요소가 깊이감 있게 비행하는 패턴입니다. 실무에서 바로 적용할 수 있는 핵심 기술을 정리했습니다.
- z축 비행 애니메이션
- perspective 500px 컨테이너에서 z:550~1350에서 0으로 수렴한 뒤 다시 발산하는 3단계 플라이 패턴
- z: interpolate(tz, 0, eased) opacity: 1 - z / 350
- 랜덤 순서 스태거
- Math.random()으로 생성한 27개 순서값으로 각 글자·단어의 등장 타이밍을 자연스럽게 분산
- staggerOrder = Array(27) .map(() => Math.random())
- 이중 텍스트 분할
- heading은 chars로, body는 words로 분할하여 서로 다른 밀도의 3D 플라이 효과 적용
- SplitText.create(heading, { type: 'chars' }) SplitText.create(body, { type: 'words' })
- Orb 구성 디자인
- main orb 30° 회전 + small orb transformOrigin 250%로 궤도 회전 — 스크롤 progress에 연동
- transformOrigin: '250% 250%' rotation: progress * 120
- 스크롤 임계값 계산
- 섹션 높이 + 뷰포트로 flyInEnd · flyOutStart 임계값을 동적 계산하여 3단계 전환점 결정
- flyInEnd = (vh * 1.3) / totalScrollPx flyOutStart = stickyRelease - offset
- 깊이 기반 투명도
- z 값이 350 이상이면 opacity가 0으로 수렴하여 먼 거리의 글자가 자연스럽게 사라지는 깊이감 연출
- opacity: Math.max(0, 1 - z / 350)
Responsive
반응형 전략
데스크탑 퍼스트로 설계하고 max-xl · max-md 2단계 브레이크포인트만 사용합니다. 3D perspective 효과는 모든 디바이스에서 유지되며, 모바일에서는 세로 스택 레이아웃으로 전환됩니다.
- 300vh 높이 3D 플라이
- 좌 45% Orb + 우 55% 텍스트
- perspective: 500px 3D 효과
- small orb 궤도 회전
- chars + words 개별 애니메이션
- 레이아웃 비율 유지
- 텍스트 크기 축소
- Orb 크기 vw 반응형
- 3D 효과 유지
- 그리드 간격 축소
- 세로 스택 레이아웃
- Orb 상단 + 텍스트 하단
- vw 기반 반응형 크기
- 3D 효과 유지
- 그리드 유지 (간격 축소)