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

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:

  1. Pin the section
  2. Animate inner content with ease: "none" (required)
  3. Attach ScrollTrigger with pin + scrub
  4. Use containerAnimation on 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()) 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.