가로 스크롤 패널 전환
Overview
세로 스크롤을 가로 이동으로 변환하는 Horizontal Scroll 섹션을 GSAP ScrollTrigger로 구현한 프로젝트입니다. Sticky 컨테이너 안에서 트랙이 수평 이동하며, 8개의 피처 카드가 뷰포트 진입 시 smoothstep 이징으로 등장합니다.
scrollWidth에서 viewportWidth를 빼서 정확한 이동량을 계산하고, 배경 Orb 드리프트 · FloatingShapes · clip-path 프로그레스 바까지 스크롤 진행률에 연동하여 풍부한 시각적 레이어를 구성합니다.
Anatomy
UI 해부도
가로 스크롤 섹션은 Sticky 뷰포트 안에 수평 트랙이 이동하는 구조입니다. 배경 Orb · FloatingShapes · 프로그레스 바가 스크롤 진행률에 연동되어 시각적 깊이감을 만듭니다.
- Sticky Container
- h-svh 고정 뷰포트
- HzTitle
- 섹션 타이틀 · 좌상단 고정
- Horizontal Track
- fit-content 너비 · x 트랜슬레이트
- Feature Card ×8
- 아이콘 · 제목 · 설명 · 그라디언트 배경
- Progress Bar
- clip-path 기반 진행 표시
- FloatingShapes
- 18개 부유 도형 · 스크롤 연동 이동
Structure
프로젝트 아키텍처
SectionHorizontal이 루트 컨테이너로서 스크롤 계산과 ScrollTrigger를 관리하고, 카드 · 이펙트 · 데이터가 폴더별로 분리되어 관심사가 명확합니다.
- SectionHorizontal.tsx
- 루트 섹션 · 스크롤 계산 · ScrollTrigger
- Card.jsx
- 피처 카드 · 아이콘 + 그라디언트 배경
- HzTitle.jsx
- 섹션 타이틀 · 좌상단 배치
- FloatingShapes.tsx
- 랜덤 부유 도형 · 시드 기반 생성
- BgOrbs.jsx
- 프리셋 배경 그라디언트 블롭
- horizontalFeatures.json
- 8개 피처 카드 데이터 (아이콘 · 색상)
Data Flow
컴포넌트 데이터 플로우
데이터는 단방향으로 흐릅니다. 마운트 시 스크롤 이동량을 계산하고, ScrollTrigger의 scrub이 매 프레임 진행률을 갱신하면 트랙 이동 · 카드 입장 · Orb 드리프트가 동시에 렌더됩니다.
ANIMATION OUTPUT
- Track
- 가로 이동
- x: -scrollAmount × progress
- Card
- 입장 애니메이션
- opacity + scale + y easing
- ProgressBar
- 진행 표시
- clip-path inset 0→100%
- BgOrbs
- 배경 드리프트
- x/y/scale 스크롤 연동
Timeline
애니메이션 타임라인
스크롤 진행률 0%→100% 동안 5개의 애니메이션 레이어가 동시에 동작합니다. ScrollTrigger scrub 기반이므로 시간이 아닌 스크롤 위치에 연동됩니다.
- 전체 트랙 x 이동 (scrollWidth - viewportWidth)
- 뷰포트 진입 시 opacity·scale·y 전환 (smoothstep)
- 배경 Orb x/y/scale 드리프트
- 18개 도형 대각선 이동 + 스크롤 연동
- clip-path inset(0 100%→0% 0 0)
Interaction
인터랙션 플로우
스크롤 이벤트가 발생하면 ScrollTrigger onUpdate가 진행률을 계산하고, 트랙 이동과 카드 입장이 동시에 분기됩니다. 카드는 뷰포트 교차 여부에 따라 smoothstep 입장 또는 대기 상태로 분기합니다.
progress 0→1- 1
scrollAmount 계산
track.scrollWidth - window.innerWidth - 2
트랙 x 이동 적용
gsap.set(track, { x: -amt * progress }) - 3
Orb x/y/scale 드리프트 동시 갱신
- 1
카드 좌측 좌표 계산
cardLeft = card.offsetLeft + trackX - 2
뷰포트 교차 판정
cardLeft < viewportWidth ? - 3
smoothstep 이징 적용
eased = p * p * (3 - 2 * p)
Patterns
핵심 기술 패턴
단순히 가로로 움직이는 것이 아니라, 프로덕션에서 안정적으로 동작하는 구조로 설계했습니다. 스크롤량 계산 · smoothstep 이징 · 리사이즈 대응까지 — 실무에서 바로 적용할 수 있는 패턴입니다.
- 가로 스크롤 변환
- 세로 스크롤을 가로 이동으로 변환 — scrollWidth에서 viewportWidth를 빼서 정확한 이동량 계산
- scrollAmount = track.scrollWidth - window.innerWidth section.style.height = scrollAmount + vh
- 커스텀 이징 함수
- 카드 입장에 smoothstep(p²(3-2p)) 적용하여 선형 보간보다 자연스러운 가속-감속 전환 구현
- eased = p * p * (3 - 2 * p)
- 고정 뷰포트 패턴
- 섹션 높이를 scrollAmount + 100vh로 설정 — sticky top:0 컨테이너가 뷰포트에 고정되며 내부만 이동
- section.height = scrollAmount + innerHeight
- 시드 기반 도형 생성
- seed 값으로 일관된 랜덤 배치 생성 — 18개 도형의 크기·위치·속도·투명도를 제어
- generateShapes(18, { seed: 77 })
- 리사이즈 대응
- ScrollTrigger refreshInit 이벤트에 scrollAmount 재계산 바인딩하여 뷰포트 변경 시 자동 보정
- ScrollTrigger.addEventListener('refreshInit', recalc)
- 그라디언트 카드 배경
- 각 카드에 개별 bg·shadow 값 지정 — 그라디언트 배경 + 박스 쉐도우로 시각적 깊이감 표현
- bg: 'linear-gradient(...)' shadow: '0 8px 32px ...'
Responsive
반응형 전략
데스크탑 퍼스트로 설계하고 max-xl · max-md 2단계 브레이크포인트만 사용합니다. 가로 스크롤 자체는 모든 디바이스에서 동작하며, 간격 · Orb 드리프트 등 세부 조정만 변경됩니다.
- 가로 스크롤 풀 동작
- 8개 카드 + gap-8 간격
- FloatingShapes 18개 표시
- BgOrb 드리프트 활성
- 하단 프로그레스 바
- 카드 크기 유지
- 간격·패딩 축소
- FloatingShapes 유지
- 프로그레스 바 유지
- 타이틀 위치 유지
- 카드 gap-4로 축소
- 트랙 좌측 패딩 축소
- 프로그레스 바 위치 조정
- Orb 드리프트 비활성
- 터치 스크롤 대응