mirror of
https://github.com/greensock/gsap-skills
synced 2026-09-22 23:34:24 +08:00
Initial commit
This commit is contained in:
+30
@@ -0,0 +1,30 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Environment
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Build outputs
|
||||
dist/
|
||||
build/
|
||||
|
||||
# Temporary files
|
||||
tmp/
|
||||
temp/
|
||||
*.tmp
|
||||
@@ -0,0 +1,27 @@
|
||||
# Guidance for AI Agents Working in This Repo
|
||||
|
||||
This repository contains **GSAP (GreenSock Animation Platform) skills** for AI coding agents. When editing or adding skills, follow these rules.
|
||||
|
||||
## Repo structure
|
||||
|
||||
- **skills/** — Each subdirectory is one skill. The CLI and agents discover skills by scanning `skills/` for directories that contain `SKILL.md`.
|
||||
- **Skill directory name** must exactly match the `name` in that skill’s frontmatter (e.g. `skills/gsap-core/` ↔ `name: gsap-core`).
|
||||
|
||||
## SKILL.md requirements
|
||||
|
||||
- **Frontmatter (YAML):**
|
||||
- `name` (required): lowercase, hyphens only, max 64 chars, must match parent directory name.
|
||||
- `description` (required): what the skill does and when to use it; include trigger terms so agents know when to apply it. Max 1024 chars.
|
||||
- `license` (optional): e.g. `MIT` if the skill is under the repo license.
|
||||
- **Body:** Markdown instructions. Keep under ~500 lines; put long reference material in `references/` or `scripts/` and link from SKILL.md.
|
||||
|
||||
## Conventions
|
||||
|
||||
- Write descriptions in **third person** (e.g. "Use when…" not "You can use when…").
|
||||
- Be concise; avoid restating general GSAP docs. Focus on correct API usage, pitfalls, and cleanup.
|
||||
- When adding a new skill: create `skills/<skill-name>/SKILL.md`, then update README.md "Skills" table and "Structure" section.
|
||||
|
||||
## References
|
||||
|
||||
- [Agent Skills specification](https://agentskills.io/specification.md)
|
||||
- [skills CLI (discovery, install)](https://github.com/vercel-labs/skills)
|
||||
@@ -1,2 +1,56 @@
|
||||
# gsap-skills
|
||||
|
||||
Official AI skills for GSAP. These skills teach AI coding agents how to correctly use the GSAP (GreenSock Animation Platform), including best practices, common animation patterns, and plugin usage.
|
||||
|
||||
Skills follow the [Agent Skills](https://agentskills.io) format and work with the [skills](https://github.com/vercel-labs/skills) CLI (Cursor, Claude Code, Codex, Windsurf, GitHub Copilot, and others).
|
||||
|
||||
## Installation
|
||||
|
||||
Install all GSAP skills with the skills CLI:
|
||||
|
||||
```bash
|
||||
npx skills add <owner>/gsap-skills
|
||||
```
|
||||
|
||||
Replace `<owner>` with the GitHub org or username (e.g. `greensock/gsap-skills`). To list skills without installing:
|
||||
|
||||
```bash
|
||||
npx skills add <owner>/gsap-skills --list
|
||||
```
|
||||
|
||||
To install a single skill by name:
|
||||
|
||||
```bash
|
||||
npx skills add <owner>/gsap-skills --skill gsap-core
|
||||
```
|
||||
|
||||
Agents use each skill when the task matches the skill’s `description` (e.g. “scroll animation” → gsap-scrolltrigger).
|
||||
|
||||
## Skills
|
||||
|
||||
| Skill | Description |
|
||||
|-------|-------------|
|
||||
| **gsap-core** | Core API: `gsap.to()` / `from()` / `fromTo()`, easing, duration, stagger, defaults |
|
||||
| **gsap-timeline** | Timelines: sequencing, position parameter, labels, nesting, playback |
|
||||
| **gsap-scrolltrigger** | ScrollTrigger: scroll-linked animations, pinning, scrub, triggers, refresh & cleanup |
|
||||
| **gsap-react** | React: useGSAP hook, refs, `gsap.context()`, cleanup, SSR |
|
||||
| **gsap-performance** | Performance: transforms over layout props, will-change, batching, ScrollTrigger tips |
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
gsap-skills/
|
||||
README.md
|
||||
AGENTS.md # Guidance for agents editing this repo
|
||||
skills/
|
||||
gsap-core/ SKILL.md
|
||||
gsap-timeline/ SKILL.md
|
||||
gsap-scrolltrigger/ SKILL.md
|
||||
gsap-react/ SKILL.md
|
||||
gsap-performance/ SKILL.md
|
||||
```
|
||||
|
||||
## Compatibility
|
||||
|
||||
- **[skills CLI](https://github.com/vercel-labs/skills)** — installs into Cursor, Claude Code, Codex, Windsurf, GitHub Copilot, and [40+ agents](https://github.com/vercel-labs/skills#supported-agents)
|
||||
- **[Agent Skills spec](https://agentskills.io/specification.md)** — SKILL.md format and discovery
|
||||
@@ -0,0 +1,146 @@
|
||||
---
|
||||
name: gsap-core
|
||||
description: Use GSAP core API correctly — gsap.to(), gsap.from(), gsap.fromTo(), easing, duration, stagger, and defaults. Use when animating DOM elements, SVG, or objects with GSAP, or when the user asks about GSAP tweens, easing, or basic animation.
|
||||
license: MIT
|
||||
---
|
||||
|
||||
# GSAP Core
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
Apply when writing or reviewing GSAP animations that use the core engine: single tweens, eases, staggers, or when explaining how GSAP tweens work.
|
||||
|
||||
## Core Tween Methods
|
||||
|
||||
- **gsap.to(targets, vars)** — animate from current state to `vars`. Most common.
|
||||
- **gsap.from(targets, vars)** — animate from `vars` to current state (good for entrances).
|
||||
- **gsap.fromTo(targets, fromVars, toVars)** — explicit start and end; no reading of current values.
|
||||
- **gsap.set(targets, vars)** — apply immediately (duration 0).
|
||||
|
||||
Always use **property names in camelCase** in the vars object (e.g. `backgroundColor`, `marginTop`, `rotationX`, `scaleY`).
|
||||
|
||||
## Common vars
|
||||
|
||||
- **duration** — seconds (default 0.5).
|
||||
- **delay** — seconds before start.
|
||||
- **ease** — string or function. Prefer built-in: `"power1.out"` (default), `"power3.inOut"`, `"back.out(1.7)"`, `"elastic.out(1, 0.3)"`, `"none"`.
|
||||
- **stagger** — number (seconds between) like `0.1` or object: `{ amount: 0.3, from: "center" }`, `{ each: 0.1, from: "random" }`.
|
||||
- **overwrite** — `false` (default), `true` (immediately kill all active tweens of the same targets), or `"auto"` (when the tween renders for the first time, only kill individual overlapping properties in other **active** tweens of the same targets).
|
||||
- **repeat** — number or `-1` for infinite.
|
||||
- **yoyo** — boolean; with repeat, alternates direction.
|
||||
- **onComplete**, **onStart**, **onUpdate** — callbacks; scoped to the Animation instance itself (Tween or Timeline).
|
||||
|
||||
## Targets
|
||||
|
||||
- **Single or Multiple**: CSS selector string, element reference, array or NodeList. GSAP handles arrays; use stagger for offset.
|
||||
|
||||
## Stagger
|
||||
|
||||
Offset the animation of each item by 0.1 second like this:
|
||||
```javascript
|
||||
gsap.to(".item", {
|
||||
y: -20,
|
||||
stagger: 0.1
|
||||
});
|
||||
```
|
||||
Or use the object syntax for advanced options like how each successive stagger amount is applied to the targets array (`from: "random" | "start" | "center" | "end" | "edges" | (index)`)
|
||||
|
||||
### Learn More
|
||||
|
||||
https://gsap.com/resources/getting-started/Staggers
|
||||
|
||||
## Easing
|
||||
|
||||
Use string eases unless a custom curve is needed:
|
||||
|
||||
```javascript
|
||||
ease: "power1.out" // default feel
|
||||
ease: "power3.inOut"
|
||||
ease: "back.out(1.7)" // overshoot
|
||||
ease: "elastic.out(1, 0.3)"
|
||||
ease: "none" // linear
|
||||
```
|
||||
|
||||
Built-in eases: base (same as `.out`), `.in`, `.out`, `.inOut` where "power" refers to the strength of the curve (1 is more gradual, 4 is steepest):
|
||||
|
||||
```
|
||||
base (out) .in .out .inOut
|
||||
"none"
|
||||
"power1" "power1.in" "power1.out" "power1.inOut"
|
||||
"power2" "power2.in" "power2.out" "power2.inOut"
|
||||
"power3" "power3.in" "power3.out" "power3.inOut"
|
||||
"power4" "power4.in" "power4.out" "power4.inOut"
|
||||
"back" "back.in" "back.out" "back.inOut"
|
||||
"bounce" "bounce.in" "bounce.out" "bounce.inOut"
|
||||
"circ" "circ.in" "circ.out" "circ.inOut"
|
||||
"elastic" "elastic.in" "elastic.out" "elastic.inOut"
|
||||
"expo" "expo.in" "expo.out" "expo.inOut"
|
||||
"sine" "sine.in" "sine.out" "sine.inOut"
|
||||
```
|
||||
|
||||
### Custom: use CustomEase (plugin)
|
||||
|
||||
Simple cubic-bezier values (as used in CSS `cubic-bezier()`):
|
||||
|
||||
```javascript
|
||||
const myEase = CustomEase.create("my-ease", ".17,.67,.83,.67");
|
||||
|
||||
gsap.to(".item", {x: 100, ease: myEase, duration: 1});
|
||||
```
|
||||
|
||||
Complex curve with any number of control points, described as normalized SVG path data:
|
||||
|
||||
```javascript
|
||||
const myEase = CustomEase.create("hop", "M0,0 C0,0 0.056,0.442 0.175,0.442 0.294,0.442 0.332,0 0.332,0 0.332,0 0.414,1 0.671,1 0.991,1 1,0 1,0");
|
||||
|
||||
gsap.to(".item", {x: 100, ease: myEase, duration: 1});
|
||||
```
|
||||
|
||||
## Returning and Controlling Tweens
|
||||
|
||||
All tween methods return a **Tween** instance. Store the return value when controlling playback is needed:
|
||||
|
||||
```javascript
|
||||
const tween = gsap.to(".box", { x: 100, duration: 1, repeat: 1, yoyo: true });
|
||||
tween.pause();
|
||||
tween.play();
|
||||
tween.reverse();
|
||||
tween.kill();
|
||||
tween.progress(0.5);
|
||||
tween.time(0.2);
|
||||
tween.totalTime(1.5);
|
||||
```
|
||||
|
||||
## Function-based values
|
||||
Use a function for a `vars` value and it will get called **once for each target** the first time the tween renders, and whatever is returned by that function will be used as the animation value.
|
||||
|
||||
```javascript
|
||||
gsap.to(".item", {
|
||||
x: (i, target, targetsArray) => i * 50, // first item animates to 0, the second to 50, the third to 100, etc.
|
||||
stagger: 0.1
|
||||
});
|
||||
```
|
||||
|
||||
## Relative values
|
||||
|
||||
Use a `+=`, `-=`, `*=`, or `/=` prefix to indicate a **relative** value. For example, the following will animate x to 20 pixels less than whatever it is when the tween renders for the first time.
|
||||
|
||||
```javascript
|
||||
gsap.to(".class", {x: "-=20" });
|
||||
```
|
||||
`x: "+=20"` would add 20 to the current value. `"*=2"` would multiply by 2, and `"/=2"` would divide by 2.
|
||||
|
||||
|
||||
## Defaults
|
||||
|
||||
Set project-wide Tween defaults with **gsap.defaults()**:
|
||||
|
||||
```javascript
|
||||
gsap.defaults({ duration: 0.6, ease: "power2.out" });
|
||||
```
|
||||
|
||||
## Do Not
|
||||
|
||||
- Animate layout-heavy properties (e.g. `width`, `height`, `top`, `left`) when **transform** properties (`x`, `y`, `scaleX`, `scaleY`, `rotation`) can achieve the same effect; prefer transforms for better performance.
|
||||
- Use invalid or non-existent ease names; stick to documented eases.
|
||||
- Forget that **gsap.from()** uses the element’s current state as the end state; the initial values in the tween will be applied immediately unless `immediateRender: false` is in the `vars`.
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
name: gsap-performance
|
||||
description: Animate efficiently with GSAP—prefer transforms, avoid layout thrashing, use will-change wisely, and batch updates. Use when optimizing GSAP animations, reducing jank, or when the user asks about animation performance, FPS, or smooth 60fps.
|
||||
license: MIT
|
||||
---
|
||||
|
||||
# GSAP Performance
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
Apply when optimizing GSAP animations for smooth 60fps, reducing layout/paint cost, or when the user asks about performance, jank, or best practices for fast animations.
|
||||
|
||||
## Prefer Transform and Opacity
|
||||
|
||||
Animating **transform** (`x`, `y`, `scaleX`, `scaleY`, `rotation`, `rotationX`, `rotationY`, `skewX`, `skewY`) and **opacity** keeps work on the compositor and avoids layout and most paint. Avoid animating layout-heavy properties when a transform can achieve the same effect.
|
||||
|
||||
- Prefer: **x**, **y**, **scale**, **rotation**, **opacity**.
|
||||
- Avoid when possible: **width**, **height**, **top**, **left**, **margin**, **padding** (they trigger layout and can cause jank).
|
||||
|
||||
GSAP’s **x** and **y** use transforms (translate) by default; use them instead of **left**/**top** for movement.
|
||||
|
||||
## will-change
|
||||
|
||||
Use **will-change** in CSS on elements that will animate. It hints the browser to promote the layer.
|
||||
|
||||
```css
|
||||
will-change: transform;
|
||||
```
|
||||
|
||||
## Batch Reads and Writes
|
||||
|
||||
GSAP batches updates internally. When mixing GSAP with direct DOM reads/writes or layout-dependent code, avoid interleaving reads and writes in a way that causes repeated layout thrashing. Prefer doing all reads first, then all writes (or let GSAP handle the writes in one go).
|
||||
|
||||
## Many Elements (Stagger, Lists)
|
||||
|
||||
- Use **stagger** instead of many separate tweens with manual delays when the animation is the same; it’s more efficient.
|
||||
- For long lists, consider **virtualization** or animating only visible items; avoid creating hundreds of simultaneous tweens if it causes jank.
|
||||
- Reuse timelines where possible; avoid creating new timelines every frame.
|
||||
|
||||
## Frequently updated properties (e.g. mouse followers)
|
||||
|
||||
Prefer **gsap.quickTo()** for properties that are updated often (e.g. mouse-follower x/y). It reuses a single tween instead of creating new tweens on each update.
|
||||
|
||||
```javascript
|
||||
let xTo = gsap.quickTo("#id", "x", { duration: 0.4, ease: "power3" }),
|
||||
yTo = gsap.quickTo("#id", "y", { duration: 0.4, ease: "power3" });
|
||||
|
||||
document.querySelector("#container").addEventListener("mousemove", (e) => {
|
||||
xTo(e.pageX);
|
||||
yTo(e.pageY);
|
||||
});
|
||||
```
|
||||
|
||||
## ScrollTrigger and Performance
|
||||
|
||||
- **pin: true** promotes the pinned element; pin only what’s needed.
|
||||
- **scrub** with a small value (e.g. `scrub: 1`) can reduce work during scroll; test on low-end devices.
|
||||
- Call **ScrollTrigger.refresh()** only when layout actually changes (e.g. after content load), not on every resize; debounce when possible.
|
||||
|
||||
## Reduce Simultaneous Work
|
||||
|
||||
- Pause or kill off-screen or inactive animations when they’re not visible (e.g. when the user navigates away).
|
||||
- Avoid animating huge numbers of properties on many elements at once; simplify or sequence if needed.
|
||||
|
||||
## Do Not
|
||||
|
||||
- Animate **width**/ **height**/ **top**/ **left** for movement when **x**/ **y**/ **scale** can achieve the same look.
|
||||
- Set **will-change** or **force3D** on every element “just in case”; use for elements that are actually animating.
|
||||
- Create hundreds of overlapping tweens or ScrollTriggers without testing on low-end devices.
|
||||
- Ignore cleanup; stray tweens and ScrollTriggers keep running and can hurt performance and correctness.
|
||||
@@ -0,0 +1,128 @@
|
||||
---
|
||||
name: gsap-react
|
||||
description: Use GSAP correctly in React — useGSAP hook, refs, gsap.context(), and cleanup. Use when animating in React, Next.js, or when the user asks about GSAP with React, useGSAP, or cleanup on unmount.
|
||||
license: MIT
|
||||
---
|
||||
|
||||
# GSAP with React
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
Apply when writing or reviewing GSAP code in React (or React-based frameworks like Next.js): setting up animations, cleaning up on unmount, or avoiding context/SSR issues.
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# Install the GSAP library
|
||||
npm install gsap
|
||||
# Install the GSAP React package
|
||||
npm install @gsap/react
|
||||
```
|
||||
|
||||
## Prefer the useGSAP() Hook
|
||||
|
||||
When **@gsap/react** is available, use the **useGSAP()** hook instead of `useEffect()` for GSAP setup. It handles cleanup automatically and provides a scope and **contextSafe** for callbacks.
|
||||
|
||||
```javascript
|
||||
import { useGSAP } from "@gsap/react";
|
||||
|
||||
gsap.registerPlugin(useGSAP); // register before running useGSAP or any GSAP code
|
||||
|
||||
const containerRef = useRef(null);
|
||||
|
||||
useGSAP(() => {
|
||||
gsap.to(".box", { x: 100 });
|
||||
gsap.from(".item", { opacity: 0, stagger: 0.1 });
|
||||
}, { scope: containerRef });
|
||||
```
|
||||
|
||||
- Pass a **scope** (ref or element) so selectors like `.box` are scoped to that root.
|
||||
- Cleanup (reverting animations and ScrollTriggers) runs automatically on unmount.
|
||||
- Use **contextSafe** from the hook's return value to wrap callbacks (e.g. onComplete) so they no-op after unmount and avoid React warnings.
|
||||
|
||||
## Refs for Targets
|
||||
|
||||
Use **refs** so GSAP targets the actual DOM nodes after render. Do not rely on selector strings that might match multiple or wrong elements across re-renders unless a `scope` is defined. With useGSAP, pass the ref as **scope**; with useEffect, pass it as the second argument to `gsap.context()`. For multiple elements, use a ref to the container and query children, or use an array of refs.
|
||||
|
||||
## Dependency array, scope, and revertOnUpdate
|
||||
|
||||
By default, useGSAP() passes an empty dependency array to the internal useEffect()/useLayoutEffect() so that it doesn't get called on every render. The 2nd argument is optional; it can pass either a dependency array (like useEffect()) or a config object for more flexibility:
|
||||
|
||||
```javascript
|
||||
useGSAP(() => {
|
||||
// gsap code here, just like in a useEffect()
|
||||
},{
|
||||
dependencies: [endX], // dependency array (optional)
|
||||
scope: container, // scope selector text (optional, recommended)
|
||||
revertOnUpdate: true // causes the context to be reverted and the cleanup function to run every time the hook re-synchronizes (when any dependency changes)
|
||||
});
|
||||
```
|
||||
|
||||
## gsap.context() in useEffect (when useGSAP isn't used)
|
||||
|
||||
It's okay to use **gsap.context()** inside a regular **useEffect()** when @gsap/react is not used or when the effect's dependency/trigger behavior is needed. When doing so, **always** call **ctx.revert()** in the effect's cleanup function so animations and ScrollTriggers are killed and inline styles are reverted. Otherwise this causes leaks and updates on detached nodes.
|
||||
|
||||
```javascript
|
||||
useEffect(() => {
|
||||
const ctx = gsap.context(() => {
|
||||
gsap.to(".box", { x: 100 });
|
||||
gsap.from(".item", { opacity: 0, stagger: 0.1 });
|
||||
}, containerRef);
|
||||
return () => ctx.revert();
|
||||
}, []);
|
||||
```
|
||||
|
||||
- Pass a **scope** (ref or element) as the second argument so selectors are scoped to that node.
|
||||
- **Always** return a cleanup that calls **ctx.revert()**.
|
||||
|
||||
## Context-Safe Callbacks
|
||||
|
||||
If GSAP-related objects get created inside functions that run AFTER the useGSAP executes (like pointer event handlers) they won't get reverted on unmount/re-render because they're not in the context. Use **contextSafe** (from useGSAP) for those functions:
|
||||
|
||||
```javascript
|
||||
const container = useRef();
|
||||
const badRef = useRef();
|
||||
const goodRef = useRef();
|
||||
|
||||
useGSAP((context, contextSafe) => {
|
||||
// ✅ safe, created during execution
|
||||
gsap.to(goodRef.current, { x: 100 });
|
||||
|
||||
// ❌ DANGER! This animation is created in an event handler that executes AFTER useGSAP() executes. It's not added to the context so it won't get cleaned up (reverted). The event listener isn't removed in cleanup function below either, so it persists between component renders (bad).
|
||||
badRef.current.addEventListener('click', () => {
|
||||
gsap.to(badRef.current, { y: 100 });
|
||||
});
|
||||
|
||||
// ✅ safe, wrapped in contextSafe() function
|
||||
const onClickGood = contextSafe(() => {
|
||||
gsap.to(goodRef.current, { rotation: 180 });
|
||||
});
|
||||
|
||||
goodRef.current.addEventListener('click', onClickGood);
|
||||
|
||||
// 👍 we remove the event listener in the cleanup function below.
|
||||
return () => {
|
||||
// <-- cleanup
|
||||
goodRef.current.removeEventListener('click', onClickGood);
|
||||
};
|
||||
},{ scope: container });
|
||||
```
|
||||
|
||||
## Server-Side Rendering (Next.js, etc.)
|
||||
|
||||
GSAP runs in the browser. Do not call gsap or ScrollTrigger during SSR.
|
||||
|
||||
- Use **useGSAP** (or useEffect) so all GSAP code runs only on the client.
|
||||
- If GSAP is imported at top level, ensure the app does not execute gsap.* or ScrollTrigger.* during server render. Dynamic import inside useEffect is an option if tree-shaking or bundle size is a concern.
|
||||
|
||||
## Do Not
|
||||
|
||||
- Animate using selector strings that can match elements outside the current component unless a `scope` is defined in useGSAP or gsap.context() so only elements inside the component are affected.
|
||||
- Skip cleanup; always revert context or revert tweens/ScrollTriggers in the effect return to avoid leaks and updates on unmounted nodes.
|
||||
- Run GSAP or ScrollTrigger during SSR; keep all usage inside client-only lifecycle (e.g. useGSAP).
|
||||
|
||||
|
||||
### Learn More
|
||||
|
||||
https://gsap.com/resources/React
|
||||
@@ -0,0 +1,129 @@
|
||||
---
|
||||
name: gsap-scrolltrigger
|
||||
description: Use ScrollTrigger for scroll-linked animations, pinning, scrub, and triggers. Use when building scroll-based animations, parallax, pinned sections, or when the user asks about ScrollTrigger, scroll animations, or pinning with GSAP.
|
||||
license: MIT
|
||||
---
|
||||
|
||||
# GSAP ScrollTrigger
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
Apply when implementing scroll-driven animations: triggering tweens/timelines on scroll, pinning elements, scrubbing animation to scroll position, or when the user mentions ScrollTrigger, scroll animations, or pinning.
|
||||
|
||||
## Registering the Plugin
|
||||
|
||||
ScrollTrigger is a plugin. After loading the script, register it once:
|
||||
|
||||
```javascript
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
```
|
||||
|
||||
## Basic Trigger
|
||||
|
||||
Tie a tween or timeline to scroll position:
|
||||
|
||||
```javascript
|
||||
gsap.to(".box", {
|
||||
x: 500,
|
||||
duration: 1,
|
||||
scrollTrigger: {
|
||||
trigger: ".box",
|
||||
start: "top center", // when top of trigger hits center of viewport
|
||||
end: "bottom center", // when the bottom of the trigger hits the center of the viewport
|
||||
toggleActions: "play reverse play reverse" // onEnter play, onLeave reverse, onEnterBack play, onLeaveBack reverse
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**start** / **end**: viewport position vs. trigger position. Format `"triggerPosition viewportPosition"`. Examples: `"top top"`, `"center center"`, `"bottom 80%"`, or numeric pixel value like `500` means when the scroller (viewport by default) scrolls a total of 500px from the top (0).
|
||||
|
||||
## Scrub
|
||||
|
||||
Scrub ties animation progress to scroll. Use for “scroll-driven” feel:
|
||||
|
||||
```javascript
|
||||
gsap.to(".box", {
|
||||
x: 500,
|
||||
scrollTrigger: {
|
||||
trigger: ".box",
|
||||
start: "top center",
|
||||
end: "bottom center",
|
||||
scrub: true // or number (smoothness delay in seconds), so 0.5 means it'd take 0.5 seconds to "catch up" to the current scroll position.
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
With **scrub: true**, the animation progresses as the user scrolls through the start–end range. Use a number (e.g. `scrub: 1`) for smooth lag.
|
||||
|
||||
## Pinning
|
||||
|
||||
Pin the trigger element while the scroll range is active:
|
||||
|
||||
```javascript
|
||||
scrollTrigger: {
|
||||
trigger: ".section",
|
||||
start: "top top",
|
||||
end: "+=1000", // pin for 1000px scroll
|
||||
pin: true,
|
||||
scrub: 1
|
||||
}
|
||||
```
|
||||
|
||||
- **pinSpacing** — default `true`; adds spacer element so layout doesn’t collapse when the pinned element is set to `position: fixed`. Set `pinSpacing: false` only when layout is handled separately.
|
||||
|
||||
|
||||
## Markers (Development)
|
||||
|
||||
Use during development to see trigger positions:
|
||||
|
||||
```javascript
|
||||
scrollTrigger: {
|
||||
trigger: ".box",
|
||||
start: "top center",
|
||||
end: "bottom center",
|
||||
markers: true
|
||||
}
|
||||
```
|
||||
|
||||
Remove or set **markers: false** for production.
|
||||
|
||||
## Timeline + ScrollTrigger
|
||||
|
||||
Drive a timeline with scroll and optional scrub:
|
||||
|
||||
```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 }).to(".c", { opacity: 0 });
|
||||
```
|
||||
|
||||
The timeline’s progress is tied to scroll through the trigger’s start/end range.
|
||||
|
||||
## Refresh and Cleanup
|
||||
|
||||
- **ScrollTrigger.refresh()** — recalculate positions (e.g. after DOM/layout changes, fonts loaded, or dynamic content).
|
||||
- When removing animated elements or changing pages (e.g. in SPAs), **kill** associated ScrollTrigger instances so they don’t run on stale elements:
|
||||
|
||||
```javascript
|
||||
ScrollTrigger.getAll().forEach(t => t.kill());
|
||||
// or kill by trigger element
|
||||
ScrollTrigger.getById("myTrigger")?.kill();
|
||||
```
|
||||
|
||||
In React, kill in a cleanup (e.g. in useEffect return) when the component unmounts.
|
||||
|
||||
## Do Not
|
||||
|
||||
- Nest ScrollTriggered animations inside of a parent timeline. ScrollTriggers should only exist on top-level animations. Wrong: `gsap.timeline().to(..., {scrollTrigger: {...}})`. Correct: `gsap.timeline({scrollTrigger: {...}}).to(...)` (the tween inside the timeline shouldn't have a ScrollTrigger).
|
||||
- Forget to **gsap.registerPlugin(ScrollTrigger)** before using ScrollTrigger.
|
||||
- Use **scrub** and **toggleActions** together on the same ScrollTrigger; choose one behavior. If both exist, scrub wins.
|
||||
- Leave **markers: true** in production.
|
||||
- Omit **refresh()** after layout changes (new content, images, fonts) that affect trigger positions. ScrollTrigger will automatically call ScrollTrigger.refresh() when the viewport resizes.
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
---
|
||||
name: gsap-timeline
|
||||
description: Create and control GSAP timelines — gsap.timeline(), position parameter, nesting, and playback controls. Use when sequencing multiple animations, choreographing keyframes, or when the user asks about GSAP timelines, sequencing, or animation order.
|
||||
license: MIT
|
||||
---
|
||||
|
||||
# GSAP Timeline
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
Apply when building multi-step animations, coordinating several tweens in sequence or parallel, or when the user asks about timelines, sequencing, or keyframe-style animation in GSAP.
|
||||
|
||||
## Creating a Timeline
|
||||
|
||||
```javascript
|
||||
const tl = gsap.timeline();
|
||||
tl.to(".a", { x: 100, duration: 1 });
|
||||
tl.to(".b", { y: 50, duration: 0.5 });
|
||||
tl.to(".c", { opacity: 0, duration: 0.3 });
|
||||
```
|
||||
|
||||
By default, tweens are **appended** one after another. Use the **position parameter** to place tweens at specific times or relative to other tweens.
|
||||
|
||||
## Position Parameter
|
||||
|
||||
Third argument (or position property in vars) controls placement:
|
||||
|
||||
- **Absolute**: `1` — start at 1 second.
|
||||
- **Relative (default)**: `"+=0.5"` — 0.5s after end; `"-=0.2"` — 0.2s before end.
|
||||
- **Label**: `"labelName"` — at that label; `"labelName+=0.3"` — 0.3s after label.
|
||||
- **Placement**: `"<"` — start when recently-added animation starts; `">"` — start when recently-added animation ends (default); `"<0.2"` — 0.2s after recently-added animation start.
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
tl.to(".a", { x: 100 }, 0); // at 0
|
||||
tl.to(".b", { y: 50 }, "+=0.5"); // 0.5s after last end
|
||||
tl.to(".c", { opacity: 0 }, "<"); // same start as previous
|
||||
tl.to(".d", { scale: 2 }, "<0.2"); // 0.2s after previous start
|
||||
```
|
||||
|
||||
## Timeline Defaults
|
||||
|
||||
Pass defaults into the timeline so all child tweens inherit:
|
||||
|
||||
```javascript
|
||||
const tl = gsap.timeline({ defaults: { duration: 0.5, ease: "power2.out" } });
|
||||
tl.to(".a", { x: 100 }).to(".b", { y: 50 }); // both use 0.5s and power2.out
|
||||
```
|
||||
|
||||
## Timeline Options (constructor)
|
||||
|
||||
- **paused: true** — create paused; call `.play()` to start.
|
||||
- **repeat**, **yoyo** — same as tweens; apply to whole timeline.
|
||||
- **onComplete**, **onStart**, **onUpdate** — timeline-level callbacks.
|
||||
- **defaults** — vars merged into every child tween.
|
||||
|
||||
## Labels
|
||||
|
||||
Add and use labels for readable, maintainable sequencing:
|
||||
|
||||
```javascript
|
||||
tl.addLabel("intro", 0);
|
||||
tl.to(".a", { x: 100 }, "intro");
|
||||
tl.addLabel("outro", "+=0.5");
|
||||
tl.to(".b", { opacity: 0 }, "outro");
|
||||
tl.play("outro"); // start from "outro"
|
||||
tl.tweenFromTo("intro", "outro"); // pauses the timeline and returns a new Tween that animates the timeline's playhead from intro to outro with no ease.
|
||||
```
|
||||
|
||||
## Nesting Timelines
|
||||
|
||||
Timelines can contain other timelines.
|
||||
|
||||
```javascript
|
||||
const master = gsap.timeline();
|
||||
const child = gsap.timeline();
|
||||
child.to(".a", { x: 100 }).to(".b", { y: 50 });
|
||||
master.add(child, 0);
|
||||
master.to(".c", { opacity: 0 }, "+=0.2");
|
||||
```
|
||||
|
||||
## Controlling Playback
|
||||
|
||||
- **tl.play()** / **tl.pause()**
|
||||
- **tl.reverse()** / **tl.progress(1)** then **tl.reverse()**
|
||||
- **tl.restart()** — from start.
|
||||
- **tl.time(2)** — seek to 2 seconds.
|
||||
- **tl.progress(0.5)** — seek to 50%.
|
||||
- **tl.kill()** — kill timeline and (by default) its children.
|
||||
|
||||
## Do Not
|
||||
|
||||
- Forget that **duration** on the timeline constructor is not the same as tween duration; timeline “duration” is determined by its children.
|
||||
- Nest animations that contain a ScrollTrigger; ScrollTriggers should only be on top-level Tweens/Timelines.
|
||||
Reference in New Issue
Block a user