Communitygithub.com

apple-scroll-animation

Apple 스타일의 스크롤 기반 애니메이션(스크롤텔링)을 순수 HTML/CSS/JS로 라이브러리 없이 만듭니다. 사용자가 Apple.com 제품 페이지처럼 스크롤에 따라 요소가 조립되거나, 빛나거나, 기울어지거나, 확장되거나, 전환되는 페이지를 원할 때 사용합니다. 고정(sticky) 섹션, 스크롤 진행률 기반 제어, 단어별 표시 효과, 요소 등장 효과, 그리고 헤드리스 테스트 방법을 다룹니다.

apple-scroll-animation란 무엇인가요?

이 스킬은 Apple 웹사이트에서 볼 수 있는 정교한 스크롤 기반 애니메이션(스크롤텔링)을 라이브러리 없이 순수 HTML, CSS, JavaScript로 구현하는 방법을 제공합니다. 모든 효과는 스크롤 위치에 따라 재생되며, 위로 스크롤하면 역재생되어 사용자 경험을 향상시킵니다. 고정(sticky) 섹션, 진행률 계산, 시각 효과(조립, 단어 강조, 기울임, 확장 등)를 조합하며, 개별 CSS 속성(`translate`, `rotate`, `scale`)을 사용하여 호버와 충돌을 방지합니다. 또한 반응형 디자인, 동작 감소 설정, 헤드리스 Chrome DevTools Protocol을 통한 테스트 방법까지 포함하여 실제 프로덕션 수준의 구현을 지원합니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/MananShukla007/apple-scroll-animation/tree/HEAD/skills/apple-scroll-animation

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

Apple 스타일 스크롤 애니메이션

효과는 타이머가 아닌 스크롤 위치에 따라 제어됩니다. 위로 스크롤하면 역재생됩니다. 라이브러리가 필요 없습니다.

세 가지 핵심 개념

  1. 고정. height: 300vh 등 큰 높이를 가진 래퍼는 position: sticky; top: 0; height: 100vh; overflow: hidden인 자식을 가집니다. 자식은 래퍼가 스크롤되는 동안 화면에 고정되어 있습니다. 래퍼의 추가 높이가 애니메이션 예산이 됩니다.
  2. 진행률. 하나의 함수가 스크롤 위치를 0..1 사이의 숫자로 변환합니다:
    • 고정 섹션: clamp(-rect.top / (rect.height - innerHeight))
    • 요소가 뷰포트에 진입할 때: clamp((innerHeight*a - rect.top) / (innerHeight*(a-b)))
  3. 숫자로 시각 효과 제어. translate, rotate, scale, opacity (및 borderRadius)를 각 프레임마다 설정합니다. requestAnimationFrame으로 조절된 하나의 scroll 리스너가 모든 것을 다시 계산합니다.

바로 사용 가능한 엔진: references/scroll-engine.js

잘 작동하는 효과 (모두가 아닌 3-5개 선택)

효과방법
조립아이템이 뷰포트 가장자리에 흩어진 상태(무작위 각도, 회전, 배율 .35, 투명도 .2)로 시작하여 레이아웃 위치로 부드럽게 이동합니다. 각 아이템의 자연스러운 위치로부터의 오프셋을 (로드/리사이즈 시) 한 번 계산한 후 (1 - ease(p))를 곱합니다. 인덱스에 따라 지연을 추가합니다.
단어 강조각 단어를 span으로 감쌉니다. clamp(p * 1.25 * N - k)에 따라 투명도가 .18에서 1로 변경됩니다.
고정 스테퍼step = floor(p * N); 분수 p*N - step은 활성 단계 내 진행률 표시줄을 채웁니다. 단계를 클릭하면 해당 단계 위치로 scrollTo됩니다.
기울임-평면전화기/기기 목업의 경우 perspective(1400px) rotateX((1-e)*22deg) translateY((1-e)*60px) scale(.84 + .16*e)를 사용합니다.
카드 등장translate: 0 (1-e)*80px; scale: .9+.1e; opacity: e, 열(i % cols)에 따라 지연됩니다.
확장비디오/이미지 scale .78 → 1 및 borderRadius 44px → 28px를 사용합니다.

진행률에 적용하기 전에 ease = t => 1 - (1-t)**3을 사용합니다.

규칙 (어렵게 배운 교훈)

  • 개별 CSS 속성 translate, rotate, scale을 제어에 사용하고, transform은 호버용으로 남겨둡니다. 그렇지 않으면 호버와 스크롤이 동일한 속성을 놓고 경쟁합니다.
  • 같은 요소에 .reveal 페이드 클래스와 제어되는 transform을 결합하지 마세요. 하나만 선택하세요.
  • 섹션 제목을 내용과 함께 고정 패널 안에 넣으세요. 패널이 내용만 담고 중앙에 위치하면 큰 모니터에서 빈 공간이 많이 생깁니다.
  • 스크롤 거리를 적절히 유지하세요. N단계 스테퍼의 경우 약 N * 55vh + 100vh, 단일 효과의 경우 240-300vh입니다. 사용자는 700vh 이상을 피곤하게 생각합니다.
  • 고정되는 것은 100vh에 맞게 제한하세요. 짧은 화면(max-height: 820px)과 폰에서는 자막과 추가 요소를 숨기고 스테퍼에서는 활성 단계만 표시합니다.
  • 동작 감소: prefers-reduced-motion: reduce가 설정된 경우 모든 곳에서 진행률 1을 반환합니다(최종 상태, 움직임 없음). 요소를 숨긴 상태로 두지 마세요.
  • 고정(sticky)은 상위 요소에 overflow가 없어야 합니다. 단, body에 overflow-x: hidden은 괜찮습니다. 고정 요소와 페이지 사이에 있는 어떤 래퍼에도 overflow: hidden을 피하세요.
  • 리사이즈 시 다시 계산 (조립 오프셋은 레이아웃에 의존합니다).
  • translate/rotate/scale/opacity/border-radius만 애니메이션하고 레이아웃 속성은 피하세요.
  • 비디오의 첫 번째/마지막 짧은 부분을 피하세요: <video> 재생을 제어할 때 짧은 클립은 빈 화면을 만들 수 있습니다.

일반적인 페이지 순서

히어로 (고정, 조립) → 일반 문장 (효과 없음) → 증거/영상 (확장) → 고정 스테퍼 → 기기 목업 (기울임-평면) → 카드 그리드 (등장) → CTA. Apple 느낌을 위해 검은색과 밝은 회색 섹션을 번갈아 사용하고 악센트 색상 하나를 사용합니다.

테스트

헤드리스 Chrome의 --screenshot은 스크롤할 수 없습니다. 대신 DevTools 프로토콜을 통해 실제 Chrome을 제어하세요:

  1. chrome --headless=new --remote-debugging-port=9333 --user-data-dir=/tmp/prof about:blank &
  2. Node (v22+에는 전역 WebSocket이 있음)에서: http://localhost:9333/json을 가져오고, 페이지의 webSocketDebuggerUrl을 열고, Emulation.setDeviceMetricsOverride, Page.navigate을 보낸 다음, 각 위치에 대해 Runtime.evaluate("scrollTo(0,Y)")를 보내고 ~700ms 기다린 후 Page.captureScreenshot을 보냅니다.
  3. 각 고정 섹션의 offsetTop과 높이(예: 0%, 60%, 100%)에서 Y를 선택합니다.
  4. 데스크톱(1440x900), 큰 화면(2000x1300), 폰(390x800)에서 확인하고 document.documentElement.scrollWidth === innerWidth인지 확인합니다(수평 오버플로우 없음).

전달 체크리스트

  • 모든 효과는 스크롤 진행률에 의해 제어되며, 역재생 가능
  • 동작 감소 대체 처리
  • 폰 레이아웃 확인, 수평 스크롤 없음
  • 고정된 스크롤 길이가 적절하며, 큰 화면에서 빈 공간 없음
  • 각 고정 섹션의 시작/중간/끝에서 스크린샷 촬영

관련 스킬