Files
hyperframes/skills/gsap/references/scrolltrigger.md
T
James RussoandClaude Opus 4.6 0a0d5d3654 refactor(skills): consolidate 15 skills into 3 (#211)
* 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>
2026-04-06 11:21:49 -07:00

148 lines
8.2 KiB
Markdown

# ScrollTrigger
## Registering
```javascript
gsap.registerPlugin(ScrollTrigger);
```
## Basic Trigger
```javascript
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
```javascript
scrollTrigger: { trigger: ".box", start: "top center", end: "bottom center", scrub: true }
```
`scrub: true` = direct link; number (e.g. `1`) = smooth lag.
## Pinning
```javascript
scrollTrigger: {
trigger: ".section", start: "top top", end: "+=1000", pin: true, scrub: 1
}
```
## Timeline + ScrollTrigger
```javascript
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.
```javascript
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:
1. Pin the section
2. Animate inner content with **ease: "none"** (required)
3. Attach ScrollTrigger with pin + scrub
4. Use `containerAnimation` on nested triggers
```javascript
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.
```javascript
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())` or `ScrollTrigger.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.