* refactor(skills): consolidate 15 skills into 3 for better trigger reliability Merge 9 GSAP skills (core, timeline, scrolltrigger, plugins, utils, react, frameworks, performance, effects) and 6 HyperFrames skills (compose, captions, tts, audio-reactive, marker-highlight, cli) into 3 consolidated skills: - `gsap` — core API + timelines + performance in SKILL.md; scrolltrigger, plugins, utils, react, frameworks, effects in references/ - `hyperframes` — composition authoring rules in SKILL.md; captions, tts, audio-reactive, marker-highlight in references/ - `hyperframes-cli` — CLI commands (init, lint, preview, render, etc.) Why: With 15 separate skills, agents must correctly trigger the right subset for any task. "Create an animated video with captions" needed 6+ skills to fire — each with ~90% trigger accuracy means ~53% chance of getting all of them. With 3 skills, that same task needs just `hyperframes` + `gsap` (~90% both fire). Progressive disclosure still works via references/ files loaded on demand. Also fixes: CLAUDE.md referenced `window.__GSAP_TIMELINE` (incorrect) — corrected to `window.__timelines`. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(cli): add --skip-skills flag to init command Allow skipping the AI coding skills installation prompt during `hyperframes init` with `--skip-skills`. Useful when skills are already installed or when the user wants to scaffold without them. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(skills): address code review feedback on consolidation Restore content lost during over-compression: - captions: fix overflow to `visible` (not hidden — clips glow effects), add container pattern warning, scale headroom formula, and self-lint placement guidance - audio-reactive: restore sampling frequency pattern (per-frame tl.call loop vs single tween) and textShadow-on-container gotcha - effects/typewriter: restore word rotation, appending words, spacing with static text, and multi-line cursor handoff patterns - effects/audio-visualizer: restore spatial mapping conventions, fetch vs inline loading, WebGL/DOM rendering approaches, and canvas layering - hyperframes-cli: restore --strict-all flag in render flags table Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): update build:copy and template for consolidated skill names - build:copy: reference skills/hyperframes, skills/hyperframes-cli, skills/gsap instead of the old 15 skill directory names - _shared/CLAUDE.md template: update skill table to consolidated names Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
8.2 KiB
ScrollTrigger
Registering
gsap.registerPlugin(ScrollTrigger);
Basic Trigger
gsap.to(".box", {
x: 500,
scrollTrigger: {
trigger: ".box",
start: "top center",
end: "bottom center",
toggleActions: "play reverse play reverse",
},
});
start/end format: "triggerPosition viewportPosition". Examples: "top top", "center center", "bottom 80%", numeric px 500, relative "+=300", "+=100%" (scroller height), "max". Wrap in clamp() (v3.12+): "clamp(top bottom)". Can be a function returning string/number.
Key Config Options
| Property | Type | Description |
|---|---|---|
| trigger | String/Element | Element whose position defines start. Required. |
| start | String/Number/Function | When active. Default "top bottom" (or "top top" if pinned). |
| end | String/Number/Function | When ends. Default "bottom top". |
| endTrigger | String/Element | Different element for end calculation. |
| scrub | Boolean/Number | Link progress to scroll. true = direct; number = catch-up seconds. |
| toggleActions | String | Four actions: onEnter, onLeave, onEnterBack, onLeaveBack. Values: play/pause/resume/reset/restart/complete/reverse/none. Default "play none none none". |
| pin | Boolean/String/Element | Pin element while active. true = pin trigger. Animate children, not the pinned element. |
| pinSpacing | Boolean/String | Default true (adds spacer). false or "margin". |
| horizontal | Boolean | For horizontal scrolling. |
| scroller | String/Element | Scroll container (default: viewport). |
| markers | Boolean/Object | Dev markers. Remove in production. |
| once | Boolean | Kill after end reached once. |
| snap | Number/Array/Function/"labels"/Object | Snap to progress values. |
| containerAnimation | Tween/Timeline | For fake horizontal scroll (see below). |
| toggleClass | String/Object | Add/remove class when active. |
| onEnter/onLeave/onEnterBack/onLeaveBack | Function | Callbacks; receive ScrollTrigger instance. |
| onUpdate/onToggle/onRefresh/onScrubComplete | Function | Progress/state callbacks. |
Standalone (no linked tween): ScrollTrigger.create({...}) with callbacks.
Scrub
scrollTrigger: { trigger: ".box", start: "top center", end: "bottom center", scrub: true }
scrub: true = direct link; number (e.g. 1) = smooth lag.
Pinning
scrollTrigger: {
trigger: ".section", start: "top top", end: "+=1000", pin: true, scrub: 1
}
Timeline + ScrollTrigger
const tl = gsap.timeline({
scrollTrigger: { trigger: ".container", start: "top top", end: "+=2000", scrub: 1, pin: true },
});
tl.to(".a", { x: 100 }).to(".b", { y: 50 });
ScrollTrigger.batch()
Creates one ScrollTrigger per target, batches callbacks within a short interval. Good for staggered reveal of many elements.
ScrollTrigger.batch(".box", {
onEnter: (elements) => gsap.to(elements, { opacity: 1, y: 0, stagger: 0.15 }),
start: "top 80%",
});
Options: interval (batch window), batchMax (max per batch). Callbacks receive (targets, scrollTriggers).
Horizontal Scroll (containerAnimation)
Pin a section, animate inner content's x/xPercent horizontally on vertical scroll:
- Pin the section
- Animate inner content with ease: "none" (required)
- Attach ScrollTrigger with pin + scrub
- Use
containerAnimationon nested triggers
const scrollTween = gsap.to(scrollingEl, {
xPercent: () => Math.max(0, window.innerWidth - scrollingEl.offsetWidth),
ease: "none",
scrollTrigger: {
trigger: scrollingEl,
pin: scrollingEl.parentNode,
start: "top top",
end: "+=1000",
},
});
gsap.to(".nested", {
y: 100,
scrollTrigger: { containerAnimation: scrollTween, trigger: ".wrapper", start: "left center" },
});
Pinning and snapping unavailable on containerAnimation-based ScrollTriggers.
ScrollTrigger.scrollerProxy()
Override scroll position reading for third-party smooth-scroll libraries. Call ScrollTrigger.update when the scroller updates.
ScrollTrigger.scrollerProxy(document.body, {
scrollTop(value) {
if (arguments.length) scrollbar.scrollTop = value;
return scrollbar.scrollTop;
},
getBoundingClientRect() {
return { top: 0, left: 0, width: window.innerWidth, height: window.innerHeight };
},
});
scrollbar.addListener(ScrollTrigger.update);
Refresh and Cleanup
ScrollTrigger.refresh()— recalculate after DOM/layout changes. Auto on resize (200ms debounce).- Create ScrollTriggers top-to-bottom or set
refreshPriority. - Kill instances when removing elements:
ScrollTrigger.getAll().forEach(t => t.kill())orScrollTrigger.getById("id")?.kill().
Do Not
- Put ScrollTrigger on child tweens inside a timeline — put on the timeline.
- Nest ScrollTriggered animations inside a parent timeline.
- Use scrub and toggleActions together (scrub wins).
- Use an ease other than "none" on the horizontal animation with containerAnimation.
- Leave markers in production.
- Create triggers in random order without refreshPriority.
- Forget refresh() after layout changes.