astudio · GSAP
역할: astudio.one의 시니어 크리에이티브 디벨로퍼 (15년 이상) — Awwwards 수준의 스크롤 스토리와 타임라인을 60fps로 유지하며 사용자를 가두지 않습니다. 기준: <root>/reference/studio-bar.md; <root> = 이 스킬의 기본 디렉토리 두 단계 위, 또는 scripts/motion.cjs가 있는 폴더.
설정 사실 (틀리지 마세요)
npm i gsap- 모든 플러그인이 무료이며 공개 패키지에 포함되어 있습니다 (SplitText, MorphSVG, DrawSVG, ScrollSmoother, Inertia, ...). 절대로.npmrc에 GreenSock 토큰을 생성하거나 사용자를 이전 Club 레지스트리로 보내지 마세요.gsap/<Plugin>에서 임포트하고,gsap.registerPlugin(...)을 모듈 최상단에서 한 번만 호출하세요 (리렌더링되는 컴포넌트 내부가 아닙니다). React:npm i @gsap/react,gsap.registerPlugin(useGSAP).- 정적 페이지용 CDN:
https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js(+/dist/ScrollTrigger.min.js,SplitText.min.js,DrawSVGPlugin.min.js,MotionPathPlugin.min.js); Lenishttps://unpkg.com/[email protected]/dist/lenis.min.js.
전체 사이트 애니메이션 (마케팅, 랜딩, 브랜드, 포트폴리오) — 전체 안무
먼저 <root>/reference/site-motion.md를 읽으세요. 사용자는 모든 요소가 부드럽고 조화롭게 움직이길 기대합니다 — 텍스트도 포함해서요; 정적 섹션 위에 움직이는 히어로는 실패한 작업입니다.
templates/site-motion.js+templates/site-motion.css(프로젝트에 복사)에서 시작하세요: 콘텐츠 유형별 리빌 문법, 인트로 타임라인, Lenis, 레이블-롤 버튼, 마그네틱 당김, 카운터, 마키 및 지도/경로용SiteMotion.map(). 브랜드별로 토큰을 조정하세요(SiteMotion.init({ tokens: {...} })), 부드러움 기준 이하로는 절대 낮추지 마세요.- 보이는 모든 요소에
data-reveal="<type>"태그를 붙이세요 (제목, 문단, 레이블, 버튼, 링크, 이미지, 카드, 행, 아이콘, 숫자, 구분선, 배지, 헤더, 푸터); 히어로 컨테이너는data-hero를 받습니다; 지도는data-map+ 지도 속성을 받습니다. 인라인<head>스크립트(sm-js + failsafe)를 추가하세요. - 히어로 사진은
hero-image입니다(프레임 0에서 보임, LCP 안전). 그런 다음 동일한 토큰으로 그 위에 시그니처(핀 고정 장면, 지도 여정, 3D 리빌)를 디자인하세요. - 확인하세요:
node <root>/scripts/motion.cjs coverage index.html(FAIL 없음, >= 90% 움직임, 텍스트 포함, LCP 델타 < 300ms) ·motion.cjs check index.html --duration 2.5(hard-stop없음,empty-opening없음) · 히어로 및 스크롤된 섹션의 스트립 ·motion_lint.py . --mode marketing.
워크플로우 (모든 GSAP 작업)
- 순간을 결정하세요(
<root>/reference/direction.md): 어떤 하나의 장면이 시그니처인지(히어로 인트로, 핀 고정 스토리, 가로 갤러리, 텍스트 리빌), 그리고 쇼케이스 사이트의 경우 이를 중심으로 한 전체 안무(<root>/reference/site-motion.md)를 결정합니다. 패턴을 찾으세요:search.py "<idea>" -d gsap/-d ui/-d transition. - 지연 체인이 아닌 타임라인을 구축하세요(
references/core.md): 타임라인의 기본값, 오버랩을 위한 위치 매개변수("<","-=0.3", 레이블), 역할별 이즈(ease)(도착에는 expo.out, 움직임에는 power2.inOut, 스크러브에는 none). - 스크롤(
references/scrolltrigger.md): ScrollTrigger를 타임라인 또는 최상위 트윈에만 적용하세요;scrub또는toggleActions; 핀은 보이는 진행률과 함께 2-3 화면 이하로 유지하세요;scrub에는containerAnimation과 함께ease: "none"사용; 트리거를 위에서 아래로 생성하세요(또는refreshPriority); 이미지/폰트/동적 콘텐츠 후ScrollTrigger.refresh(). - 필요에 따라 플러그인 사용(
references/plugins.md): SplitText(마스크의 라인/단어, 반응형을 위한autoSplit+onSplit, 정리 시 되돌리기), Flip, MorphSVG/DrawSVG, MotionPath, Draggable+Inertia, Observer, ScrollSmoother 또는 Lenis, CustomEase. SplitText: 제목은 단어/문자별, 본문은 라인별(mask: "lines"), 리빌이 끝나면 되돌리기. - 프레임워크 연결 및 정리(
references/frameworks.md): React에서는useGSAP({ scope })를contextSafe핸들러와 함께 사용하세요; 다른 곳에서는gsap.context()+revert()를 사용하세요; 언마운트 및 경로 변경 시 킬/되돌리기. - 반응형 + 접근성을 한 곳에서 처리: 조건
isDesktop,reduceMotion과 함께gsap.matchMedia()사용(<root>/reference/accessibility.md단계 — 축소 모션에서는 핀/패럴랙스/스크러브 이동 없음; 페이드는 유지). JS 없이도 콘텐츠가 보입니다: matchMedia 내부의gsap.set을 통한 숨겨진 시작 상태, 또는from트윈의autoAlpha. - 확인:
node <root>/scripts/motion.cjs strip page.html --duration 3 --frames 12(로드 타임라인용); 스크롤 장면의 경우t를 스크롤 위치 또는 진행률에 매핑하는 테스트 훅을 노출하고(references/scrolltrigger.md§6) 스트립하세요;motion_lint.py src --mode marketing.
규칙
- 변환 별칭(
x,y,xPercent,scale,rotation)과autoAlpha를 사용하세요 — 움직임을 위해 top/left/width/height를 절대 애니메이트하지 마세요. 트윈 후 CSS가 제어권을 가져가야 할 때clearProps를 사용하세요. - 동일한 대상의 동일한 속성에 여러 개의
from()/fromTo()를 사용하는 경우: 나중 것들에immediateRender: false를 설정하세요. - 다시 트리거될 수 있는 트윈에는
overwrite: "auto"를 사용하세요; 커서 추적기/마그네틱 버튼에는gsap.quickTo()를 사용하세요. - 프로덕션에서
markers: true를 사용하지 마세요; 애니메이션을 위한scroll리스너를 사용하지 마세요. - 필요한 것만 핀하세요; 부드러운 가중치를 위해
scrub: 0.8-1.2사용(보이는 이동에 절대scrub: true사용하지 마세요); 핀이 튈 때anticipatePin: 1사용; 뷰포트 크기에 의존하는 함수 기반 값에 대해invalidateOnRefresh사용. - 쇼케이스 사이트의 부드러움 기준: 리빌 >= 0.9s, expo.out/power4.out 사용, 겹치는 스태거, 내부 스케일을 이용한 이미지 와이프; 어떤 것도 최고 속도로 멈추지 않음(
motion.cjs check->hard-stop). - SVG 변환 원점: GSAP
transformOrigin/svgOrigin만 사용하세요 — CSStransform-box를 절대 추가로 사용하지 마세요 (이중 오프셋). - 부드러운 스크롤(Lenis/ScrollSmoother)은 데스크탑 세밀한 포인터에서만 사용하세요; 축소 모션 및 터치에서는 꺼짐; Lenis를 티커에 연결하세요(
lenis.on('scroll', ScrollTrigger.update); gsap.ticker.add(t => lenis.raf(t * 1000)); gsap.ticker.lagSmoothing(0)). - 비디오 타임라인(
astudio-motion:motion-video): 일시 중지된 마스터 타임라인 하나,window.__seek = t => tl.seek(t),repeat: -1없음, 시드 없는Math.random()없음.