[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6JGSFP9GFC2ZGYR6PERGZA7":3,"skill-same:01M6JGSFP9GFC2ZGYR6PERGZA7":67},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":39,"linkOnly":40,"markdownContent":41,"markdownBody":42,"frontMatter":43,"refName":44,"commitSha":45,"skillDirectory":46,"forksCount":47,"repositoryHtmlUrl":48,"repositoryHomepageUrl":49,"ownerHtmlUrl":50,"ownerBlogUrl":6,"ownerDescription":51,"updatedAt":52,"install":53,"resources":66},"01M6JGSFP9GFC2ZGYR6PERGZA7","gsap-plugins",null,"Official GSAP skill for GSAP plugins — registration, ScrollToPlugin, ScrollSmoother, Flip, Draggable, Inertia, Observer, SplitText, ScrambleText, SVG and physics plugins, CustomEase, EasePack, CustomWiggle, CustomBounce, GSDevTools. Use when the user asks about a GSAP plugin, scroll-to, flip animations, draggable, SVG drawing, or plugin registration.","ancyloce\u002Fanvilkit-studio","ancyloce","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F103255835?v=4","01M6JGS0GSF9KTQE3VS6HA5FZ2","Ancyloce","01M6JGS0NK83XM8EJYT39C39CD",2,"MIT","https:\u002F\u002Fgithub.com\u002Fancyloce\u002Fanvilkit-studio\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fgsap-plugins\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000016","图像与多媒体","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"gsap","plugins","gsap plugins","official","plugin","user","use","registration","drawing","physics","scroll","SVG","inertia","about","for","2026-11-10 10:12:30",false,"---\nname: gsap-plugins\ndescription: Official GSAP skill for GSAP plugins — registration, ScrollToPlugin, ScrollSmoother, Flip, Draggable, Inertia, Observer, SplitText, ScrambleText, SVG and physics plugins, CustomEase, EasePack, CustomWiggle, CustomBounce, GSDevTools. Use when the user asks about a GSAP plugin, scroll-to, flip animations, draggable, SVG drawing, or plugin registration.\nlicense: MIT\n---\n\n# GSAP Plugins\n\n## When to Use This Skill\n\nApply when using or reviewing code that uses GSAP plugins: registering plugins, scroll-to, flip\u002FFLIP animations, draggable elements, SVG (DrawSVG, MorphSVG, MotionPath), text (SplitText, ScrambleText), physics, easing plugins (CustomEase, EasePack, CustomWiggle, CustomBounce), or GSDevTools. ScrollTrigger has its own skill (gsap-scrolltrigger).\n\n**Related skills:** For core tweens use **gsap-core**; for ScrollTrigger use **gsap-scrolltrigger**; for React use **gsap-react**.\n\n## Licensing & Install (important)\n\nEvery GSAP plugin is **free**, including for commercial use. Since [Webflow's acquisition of GSAP](https:\u002F\u002Fgsap.com\u002Fblog\u002Fwebflow-GSAP\u002F), Club GSAP is no longer a paid tier and **no plugin requires a membership, license key, or auth token** — this includes formerly Club-only plugins (**SplitText**, **MorphSVG**, etc.).\n\n- ✅ Install everything from the public `gsap` npm package: `npm install gsap`. All plugins are included — import them as `gsap\u002FSplitText`, `gsap\u002FMorphSVGPlugin`, etc.\n- ❌ Do **not** generate an `.npmrc` with a GreenSock auth token, suggest the private `npm.greensock.com` registry, or tell users to sign up for Club GSAP to access a plugin. Those instructions are outdated.\n\n## Registering Plugins\n\nRegister each plugin once so GSAP (and bundlers) know to include it. Use **gsap.registerPlugin()** with every plugin used in the project:\n\n```javascript\nimport gsap from \"gsap\";\nimport { ScrollToPlugin } from \"gsap\u002FScrollToPlugin\";\nimport { Flip } from \"gsap\u002FFlip\";\nimport { Draggable } from \"gsap\u002FDraggable\";\n\ngsap.registerPlugin(ScrollToPlugin, Flip, Draggable);\n```\n\n- ✅ Register before using the plugin in any tween or API call.\n- ✅ In React, register at top level or once in the app (e.g. before first useGSAP); do not register inside a component that re-renders. useGSAP is a plugin that needs to be registered before use.\n\n## Scroll\n\n### ScrollToPlugin\n\nAnimates scroll position (window or a scrollable element). Use for “scroll to element” or “scroll to position” without ScrollTrigger.\n\n```javascript\ngsap.registerPlugin(ScrollToPlugin);\n\ngsap.to(window, { duration: 1, scrollTo: { y: 500 } });\ngsap.to(window, { duration: 1, scrollTo: { y: \"#section\", offsetY: 50 } });\ngsap.to(scrollContainer, { duration: 1, scrollTo: { x: \"max\" } });\n```\n\n**ScrollToPlugin — key config (scrollTo object):**\n\n| Option | Description |\n|--------|-------------|\n| `x`, `y` | Target scroll position (number), or `\"max\"` for maximum |\n| `element` | Selector or element to scroll to (for scroll-into-view) |\n| `offsetX`, `offsetY` | Offset in pixels from the target position |\n\n### ScrollSmoother\n\nSmooth scroll wrapper (smooths native scroll). Requires ScrollTrigger and a specific DOM structure (content wrapper + smooth wrapper). Use when smooth, momentum-style scroll is needed. See GSAP docs for setup; register after ScrollTrigger. DOM structure would look like: \n\n```html\n\u003Cbody>\n\t\u003Cdiv id=\"smooth-wrapper\">\n\t\t\u003Cdiv id=\"smooth-content\">\n\t\t\t\u003C!--- ALL YOUR CONTENT HERE --->\n\t\t\u003C\u002Fdiv>\n\t\u003C\u002Fdiv>\n\t\u003C!-- position: fixed elements can go outside --->\n\u003C\u002Fbody>\n```\n\n## DOM \u002F UI\n\n### Flip\n\nCapture state with `Flip.getState()`, then apply changes (e.g. layout or class changes), then use `Flip.from()` to animate from the previous state to the new state (FLIP: First, Last, Invert, Play). Use when animating between two layout states (lists, grids, expanded\u002Fcollapsed).\n\n```javascript\ngsap.registerPlugin(Flip);\n\nconst state = Flip.getState(\".item\");\n\u002F\u002F change DOM (reorder, add\u002Fremove, change classes)\nFlip.from(state, { duration: 0.5, ease: \"power2.inOut\" });\n```\n\n**Flip — key config (Flip.from vars):**\n\n| Option | Description |\n|--------|-------------|\n| `absolute` | Use `position: absolute` during the flip (default: `false`) |\n| `nested` | When true, only the first level of children is measured (better for nested transforms) |\n| `scale` | When true, scale elements to fit (avoids stretch); default `true` |\n| `simple` | When true, only position\u002Fscale are animated (faster, less accurate) |\n| `duration`, `ease` | Standard tween options |\n\n#### More information\n\nhttps:\u002F\u002Fgsap.com\u002Fdocs\u002Fv3\u002FPlugins\u002FFlip\n\n### Draggable\n\nMakes elements draggable, spinnable, or throwable with mouse\u002Ftouch. Use for sliders, cards, reorderable lists, or any drag interaction.\n\n```javascript\ngsap.registerPlugin(Draggable, InertiaPlugin);\n\nDraggable.create(\".box\", { type: \"x,y\", bounds: \"#container\", inertia: true });\nDraggable.create(\".knob\", { type: \"rotation\" });\n```\n\n**Draggable — key config options:**\n\n| Option | Description |\n|--------|-------------|\n| `type` | `\"x\"`, `\"y\"`, `\"x,y\"`, `\"rotation\"`, `\"scroll\"` |\n| `bounds` | Element, selector, or `{ minX, maxX, minY, maxY }` to constrain drag |\n| `inertia` | `true` to enable throw\u002Fmomentum (requires InertiaPlugin) |\n| `edgeResistance` | 0–1; resistance when dragging past bounds |\n| `cursor` | CSS cursor during drag |\n| `onDragStart`, `onDrag`, `onDragEnd` | Callbacks; receive event and target |\n| `onThrowUpdate`, `onThrowComplete` | Callbacks when inertia is active |\n\n### Inertia (InertiaPlugin)\n\nWorks with Draggable for momentum after release, or track the inertia\u002Fvelocity of any property of any object so that it can then seamlessly glide to a stop using a simple tween. Register with Draggable when using `inertia: true`:\n\n```javascript\ngsap.registerPlugin(Draggable, InertiaPlugin);\nDraggable.create(\".box\", { type: \"x,y\", inertia: true });\n```\n\nOr track velocity of a property: \n```javascript\nInertiaPlugin.track(\".box\", \"x\");\n```\n\nThen use `\"auto\"` to continue the current velocity and glide to a stop: \n\n```javascript\ngsap.to(obj, { inertia: { x: \"auto\" } });\n```\n\n### Observer\n\nNormalizes pointer and scroll input across devices. Use for swipe, scroll direction, or custom gesture logic without tying directly to scroll position like ScrollTrigger.\n\n```javascript\ngsap.registerPlugin(Observer);\n\nObserver.create({\n  target: \"#area\",\n  onUp: () => {},\n  onDown: () => {},\n  onLeft: () => {},\n  onRight: () => {},\n  tolerance: 10\n});\n```\n\n**Observer — key config options:**\n\n| Option | Description |\n|--------|-------------|\n| `target` | Element or selector to observe |\n| `onUp`, `onDown`, `onLeft`, `onRight` | Callbacks when swipe\u002Fscroll passes tolerance in that direction |\n| `tolerance` | Pixels before direction is detected; default 10 |\n| `type` | `\"touch\"`, `\"pointer\"`, or `\"wheel\"` (default: `\"touch,pointer\"`) |\n\n## Text\n\n### SplitText\n\nSplits an element’s text into characters, words, and\u002For lines (each in its own element) for staggered or per-unit animation. Use when animating text character-by-character, word-by-word, or line-by-line. Returns an instance with **chars**, **words**, **lines** (and **masks** when `mask` is set). Restore original markup with **revert()** or let **gsap.context()** revert. Integrates with **gsap.context()**, **matchMedia()**, and **useGSAP()**. API: **SplitText.create(target, vars)** (target = selector, element, or array).\n\n```javascript\ngsap.registerPlugin(SplitText);\n\nconst split = SplitText.create(\".heading\", { type: \"words, chars\" });\ngsap.from(split.chars, { opacity: 0, y: 20, stagger: 0.03, duration: 0.4 });\n\u002F\u002F later: split.revert() or let gsap.context() cleanup revert\n```\n\nWith **onSplit()** (v3.13.0+), animations run on each split and on re-split when **autoSplit** is used; returning a tween\u002Ftimeline from **onSplit()** lets SplitText clean up and sync progress on re-split:\n\n```javascript\nSplitText.create(\".split\", {\n  type: \"lines\",\n  autoSplit: true,\n  onSplit(self) {\n    return gsap.from(self.lines, { y: 100, opacity: 0, stagger: 0.05, duration: 0.5 });\n  }\n});\n```\n\n**SplitText — key config (SplitText.create vars):**\n\n| Option | Description |\n|--------|-------------|\n| **type** | Comma-separated: `\"chars\"`, `\"words\"`, `\"lines\"`. Default `\"chars,words,lines\"`. Only split what is needed (e.g. `\"words, chars\"` if not using lines) for performance. Avoid chars-only without words\u002Flines or use **smartWrap: true** to prevent odd line breaks. |\n| **charsClass**, **wordsClass**, **linesClass** | CSS class on each split element. Append `\"++\"` to add an incremented class (e.g. `linesClass: \"line++\"` → `line1`, `line2`, …). |\n| **aria** | `\"auto\"` (default), `\"hidden\"`, or `\"none\"`. Accessibility: `\"auto\"` adds `aria-label` on the split element and `aria-hidden` on line\u002Fword\u002Fchar elements so screen readers read the label; `\"hidden\"` hides all from readers; `\"none\"` leaves aria unchanged. Use `\"none\"` plus a screen-reader-only duplicate if nested links\u002Fsemantics must be exposed. |\n| **autoSplit** | When `true`, reverts and re-splits when fonts finish loading or when the element width changes (and lines are split), avoiding wrong line breaks. **Animations must be created inside onSplit()** so they target the newly split elements; **return** the animation from **onSplit()** for automatic cleanup and time-sync on re-split. |\n| **onSplit(self)** | Callback when split completes (and on each re-split if **autoSplit** is `true`). Receives the SplitText instance. Returning a GSAP tween or timeline enables automatic revert\u002Fsync of that animation when re-splitting. |\n| **mask** | `\"lines\"`, `\"words\"`, or `\"chars\"`. Wraps each unit in an extra element with `overflow: clip` for mask\u002Freveal effects. Only one type; access wrappers on the instance’s **masks** array (or use class `-mask` if a class is set). |\n| **tag** | Wrapper element tag; default `\"div\"`. Use `\"span\"` for inline (note: transforms like rotation\u002Fscale may not render on inline elements in some browsers). |\n| **deepSlice** | When `true` (default), nested elements (e.g. `\u003Cstrong>`) that span multiple lines are subdivided so lines don’t stretch vertically. Only applies when splitting lines. |\n| **ignore** | Selector or element(s) to leave unsplit (e.g. `ignore: \"sup\"`). |\n| **smartWrap** | When splitting **chars** only, wraps words in a `white-space: nowrap` span to avoid mid-word line breaks. Ignored if words or lines are split. Default `false`. |\n| **wordDelimiter** | Word boundary: string (default `\" \"`), RegExp, or `{ delimiter: RegExp, replaceWith: string }` for custom splitting (e.g. zero-width joiner for hashtags, or non-Latin). |\n| **prepareText(text, parent)** | Function that receives raw text and parent element; return modified text before splitting (e.g. to insert break markers for languages without spaces). |\n| **propIndex** | When `true`, adds a CSS variable with index on each split element (e.g. `--word: 1`, `--char: 2`). |\n| **reduceWhiteSpace** | Collapse consecutive spaces; default `true`. From v3.13.0 also honors line breaks and can insert `\u003Cbr>` for `\u003Cpre>`. |\n| **onRevert** | Callback when the instance is reverted. |\n\n**Tips:** Split only what is animated (e.g. skip chars if only animating words). For custom fonts, split after they load (e.g. `document.fonts.ready.then(...)`) or use **autoSplit: true** with **onSplit()**. To avoid kerning shift when splitting chars, use CSS `font-kerning: none; text-rendering: optimizeSpeed;`. Avoid `text-wrap: balance`; it can interfere with splitting. SplitText does not support SVG `\u003Ctext>`.\n\n**Learn more:** [SplitText](https:\u002F\u002Fgsap.com\u002Fdocs\u002Fv3\u002FPlugins\u002FSplitText\u002F)\n\n### ScrambleText\n\nAnimates text with a scramble\u002Fglitch effect. Use when revealing or transitioning text with a scramble.\n\n```javascript\ngsap.registerPlugin(ScrambleTextPlugin);\n\ngsap.to(\".text\", {\n  duration: 1,\n  scrambleText: { text: \"New message\", chars: \"01\", revealDelay: 0.5 }\n});\n```\n\n## SVG\n\n### DrawSVG (DrawSVGPlugin)\n\nReveals or hides the stroke of SVG elements by animating `stroke-dashoffset` \u002F `stroke-dasharray`. Works on `\u003Cpath>`, `\u003Cline>`, `\u003Cpolyline>`, `\u003Cpolygon>`, `\u003Crect>`, `\u003Cellipse>`. Use when “drawing” or “erasing” strokes.\n\n**drawSVG value:** Describes the **visible segment** of the stroke along the path (start and end positions), not “animate from A to B over time.” Format: `\"start end\"` in percent or length. Examples: `\"0% 100%\"` = full stroke; `\"20% 80%\"` = stroke only between 20% and 80% (gaps at both ends). The tween animates from the element’s **current** segment to the **target** segment — e.g. `gsap.to(\"#path\", { drawSVG: \"0% 100%\" })` goes from whatever it is now to full stroke. Single value (e.g. `0`, `\"100%\"`) means start is 0: `\"100%\"` is equivalent to `\"0% 100%\"`. \n\n**Required:** The element must have a visible stroke — set `stroke` and `stroke-width` in CSS or as SVG attributes; otherwise nothing is drawn.\n\n```javascript\ngsap.registerPlugin(DrawSVGPlugin);\n\n\u002F\u002F draw from nothing to full stroke\ngsap.from(\"#path\", { duration: 1, drawSVG: 0 });\n\u002F\u002F or explicit segment: from 0–0 to 0–100%\ngsap.fromTo(\"#path\", { drawSVG: \"0% 0%\" }, { drawSVG: \"0% 100%\", duration: 1 });\n\u002F\u002F stroke only in the middle (gaps at ends)\ngsap.to(\"#path\", { duration: 1, drawSVG: \"20% 80%\" });\n```\n\n**Caveats:** Only affects stroke (not fill). Prefer single-segment `\u003Cpath>` elements; multi-segment paths can render oddly in some browsers. Contents of `\u003Cuse>` cannot be visually changed. **DrawSVGPlugin.getLength(element)** and **DrawSVGPlugin.getPosition(element)** return stroke length and current position.\n\n**Learn more:** [DrawSVG](https:\u002F\u002Fgsap.com\u002Fdocs\u002Fv3\u002FPlugins\u002FDrawSVGPlugin)\n\n### MorphSVG (MorphSVGPlugin)\n\nMorphs one SVG shape into another by animating the `d` attribute (path data). Start and end shapes do not need the same number of points — MorphSVG converts to cubic beziers and adds points as needed. Use for icon-to-icon morphs, shape transitions, or path-based animations. Works on `\u003Cpath>`, `\u003Cpolyline>`, and `\u003Cpolygon>`; `\u003Ccircle>`, `\u003Crect>`, `\u003Cellipse>`, and `\u003Cline>` are converted internally or via **MorphSVGPlugin.convertToPath(selector | element)** (replaces the element in the DOM with a `\u003Cpath>`).\n\n**morphSVG value:** Can be a **selector** (e.g. `\"#lightning\"`), an **element**, **raw path data** (e.g. `\"M47.1,0.8 73.3,0.8...\"`), or for polygon\u002Fpolyline a **points string** (e.g. `\"240,220 240,70 70,70 70,220\"`). For full config use the **object form** with **shape** as the only required property.\n\n```javascript\ngsap.registerPlugin(MorphSVGPlugin);\n\n\u002F\u002F convert primitives to path first if needed:\nMorphSVGPlugin.convertToPath(\"circle, rect, ellipse, line\");\n\ngsap.to(\"#diamond\", { duration: 1, morphSVG: \"#lightning\", ease: \"power2.inOut\" });\n\u002F\u002F object form:\ngsap.to(\"#diamond\", {\n  duration: 1,\n  morphSVG: { shape: \"#lightning\", type: \"rotational\", shapeIndex: 2 }\n});\n\n```\n\n**MorphSVG — key config (morphSVG object):**\n\n| Option | Description |\n|--------|-------------|\n| **shape** | _(Required.)_ Target shape: selector, element, or raw path string. |\n| **type** | `\"linear\"` (default) or `\"rotational\"`. Rotational uses angle\u002Flength interpolation and can avoid kinks mid-morph; try it when linear looks wrong. |\n| **map** | How segments are matched: `\"size\"` (default), `\"position\"`, or `\"complexity\"`. Use when start\u002Fend segments don’t line up; if none work, split into multiple paths and morph each. |\n| **shapeIndex** | Offsets which point in the start path maps to the first point in the end path (avoids shape “crossing over” or inverting). Number for single-segment paths; **array** for multi-segment (e.g. `[5, 1, -8]`). Negative reverses that segment. Use **shapeIndex: \"log\"** once to log the auto-calculated value, then paste the number\u002Farray into the tween. **findShapeIndex(start, end)** (separate utility) provides an interactive UI to find a good value. Only applies to closed paths. |\n| **smooth** | (v3.14+). Adds smoothing points. Number (e.g. `80`), `\"auto\"`, or object: `{ points: 40 \\| \"auto\", redraw: true \\| false, persist: true \\| false }`. `redraw: false` keeps original anchors (perfect fidelity, less even spacing). `persist: false` removes added points when the tween ends. Use when the default morph looks jagged or unnatural. |\n| **curveMode** | Boolean (v3.14+). Interpolates control-handle angle\u002Flength instead of raw x\u002Fy to avoid kinks on curves. Try if a morph has a mid-morph kink. |\n| **origin** | Rotation origin for **type: \"rotational\"**. String: `\"50% 50%\"` (default) or `\"20% 60%, 35% 90%\"` for different start\u002Fend origins. |\n| **precision** | Decimal places for output path data; default `2`. |\n| **precompile** | Array of precomputed path strings (or use **precompile: \"log\"** once, copy from console). Skips expensive startup calculations; use for very complex morphs. Only for `\u003Cpath>` (convert polygon\u002Fpolyline first). |\n| **render** | Function(rawPath, target) called each update — e.g. draw to canvas. RawPath is an array of segments (each segment = array of alternating x,y cubic bezier coords). |\n| **updateTarget** | When using **render** (e.g. canvas-only), set **updateTarget: false** so the original `\u003Cpath>` is not updated. **MorphSVGPlugin.defaultUpdateTarget** sets default. |\n\n**Utilities:** **MorphSVGPlugin.convertToPath(selector | element)** converts circle\u002Frect\u002Fellipse\u002Fline\u002Fpolygon\u002Fpolyline to `\u003Cpath>` in the DOM. **MorphSVGPlugin.rawPathToString(rawPath)** and **stringToRawPath(d)** convert between path strings and raw arrays. The plugin stores the original `d` on the target (e.g. for tweening back: `morphSVG: \"#originalId\"` or the same element).\n\n**Tips:** For twisted or inverted morphs, set **shapeIndex** (use `\"log\"` or findShapeIndex()). For multi-segment paths, **shapeIndex** is an array (one value per segment). Precompile only when the first frame is slow; it does not fix jank during the tween (simplify the SVG or reduce size if needed).\n\n**Learn more:** [MorphSVG](https:\u002F\u002Fgsap.com\u002Fdocs\u002Fv3\u002FPlugins\u002FMorphSVGPlugin)\n\n### MotionPath (MotionPathPlugin)\n\nAnimates an element along an SVG path. Use when moving an object along a path (e.g. a curve or custom route).\n\n```javascript\ngsap.registerPlugin(MotionPathPlugin);\n\ngsap.to(\".dot\", {\n  duration: 2,\n  motionPath: { path: \"#path\", align: \"#path\", alignOrigin: [0.5, 0.5] }\n});\n```\n\n**MotionPath — key config (motionPath object):**\n\n| Option | Description |\n|--------|-------------|\n| `path` | SVG path element, selector, or path data string |\n| `align` | Path element or selector to align the target to |\n| `alignOrigin` | `[x, y]` origin (0–1); default `[0.5, 0.5]` |\n| `autoRotate` | Rotate element to follow path tangent |\n| `curviness` | 0–2; path smoothing |\n\n### MotionPathHelper\n\nVisual editor for MotionPath (alignment, offset). Use during development to tune path alignment.\n\n```javascript\ngsap.registerPlugin(MotionPathPlugin, MotionPathHelperPlugin);\n\nconst helper = MotionPathHelper.create(\".dot\", \"#path\", { end: 0.5 });\n\u002F\u002F adjust in UI, then use helper.path or helper.getProgress() in your animation\n```\n\n## Easing\n\n### CustomEase\n\nCustom easing curves (cubic-bezier or SVG path). Use when a built-in ease is not enough. Basic usage is covered in gsap-core; register when using:\n\n```javascript\ngsap.registerPlugin(CustomEase);\nconst ease = CustomEase.create(\"name\", \".17,.67,.83,.67\");\ngsap.to(\".el\", { x: 100, ease: ease, duration: 1 });\n```\n\n### EasePack\n\nAdds more named eases (e.g. SlowMo, RoughEase, ExpoScaleEase). Register and use the ease names in tweens.\n\n### CustomWiggle\n\nWiggle\u002Fshake easing. Use when a value should “wiggle” (multiple oscillations).\n\n### CustomBounce\n\nBounce-style easing with configurable strength.\n\n## Physics\n\n### Physics2D (Physics2DPlugin)\n\n2D physics (velocity, angle, gravity). Use when animating with simple physics (e.g. projectiles, bouncing).\n\n```javascript\ngsap.registerPlugin(Physics2DPlugin);\n\ngsap.to(\".ball\", {\n  duration: 2,\n  physics2D: {\n    velocity: 250,\n    angle: 80,\n    gravity: 500\n  }\n});\n```\n\n### PhysicsProps (PhysicsPropsPlugin)\n\nApplies physics to property values. Use for physics-driven property animation.\n\n```javascript\ngsap.registerPlugin(PhysicsPropsPlugin);\n\ngsap.to(\".obj\", {\n  duration: 2,\n  physicsProps: {\n    x: { velocity: 100, end: 300 },\n    y: { velocity: -50, acceleration: 200 }\n  }\n});\n```\n\n## Development\n\n### GSDevTools\n\nUI for scrubbing timelines, toggling animations, and debugging. Use during development only; do not ship. Register and create an instance with a timeline reference.\n\n```javascript\ngsap.registerPlugin(GSDevTools);\nGSDevTools.create({ animation: tl });\n```\n\n## Other\n\n### Pixi (PixiPlugin)\n\nIntegrates GSAP with PixiJS for animating Pixi display objects. Register when animating Pixi objects with GSAP.\n\n```javascript\ngsap.registerPlugin(PixiPlugin);\n\nconst sprite = new PIXI.Sprite(texture);\ngsap.to(sprite, { pixi: { x: 200, y: 100, scale: 1.5 }, duration: 1 });\n```\n\n## Best practices\n\n- ✅ Register every plugin used with **gsap.registerPlugin()** before first use.\n- ✅ Use **Flip.getState()** → DOM change → **Flip.from()** for layout transitions; use **Draggable** + **InertiaPlugin** for drag with momentum.\n- ✅ Revert plugin instances (e.g. `SplitTextInstance.revert()`) when components unmount or elements are removed.\n\n## Do Not\n\n- ❌ Use a plugin in a tween or API without registering it first (**gsap.registerPlugin()**).\n- ❌ Ship GSDevTools or development-only plugins to production.\n\n### Learn More\n\nhttps:\u002F\u002Fgsap.com\u002Fdocs\u002Fv3\u002FPlugins\u002F\n","# GSAP Plugins\n\n## When to Use This Skill\n\nApply when using or reviewing code that uses GSAP plugins: registering plugins, scroll-to, flip\u002FFLIP animations, draggable elements, SVG (DrawSVG, MorphSVG, MotionPath), text (SplitText, ScrambleText), physics, easing plugins (CustomEase, EasePack, CustomWiggle, CustomBounce), or GSDevTools. ScrollTrigger has its own skill (gsap-scrolltrigger).\n\n**Related skills:** For core tweens use **gsap-core**; for ScrollTrigger use **gsap-scrolltrigger**; for React use **gsap-react**.\n\n## Licensing & Install (important)\n\nEvery GSAP plugin is **free**, including for commercial use. Since [Webflow's acquisition of GSAP](https:\u002F\u002Fgsap.com\u002Fblog\u002Fwebflow-GSAP\u002F), Club GSAP is no longer a paid tier and **no plugin requires a membership, license key, or auth token** — this includes formerly Club-only plugins (**SplitText**, **MorphSVG**, etc.).\n\n- ✅ Install everything from the public `gsap` npm package: `npm install gsap`. All plugins are included — import them as `gsap\u002FSplitText`, `gsap\u002FMorphSVGPlugin`, etc.\n- ❌ Do **not** generate an `.npmrc` with a GreenSock auth token, suggest the private `npm.greensock.com` registry, or tell users to sign up for Club GSAP to access a plugin. Those instructions are outdated.\n\n## Registering Plugins\n\nRegister each plugin once so GSAP (and bundlers) know to include it. Use **gsap.registerPlugin()** with every plugin used in the project:\n\n```javascript\nimport gsap from \"gsap\";\nimport { ScrollToPlugin } from \"gsap\u002FScrollToPlugin\";\nimport { Flip } from \"gsap\u002FFlip\";\nimport { Draggable } from \"gsap\u002FDraggable\";\n\ngsap.registerPlugin(ScrollToPlugin, Flip, Draggable);\n```\n\n- ✅ Register before using the plugin in any tween or API call.\n- ✅ In React, register at top level or once in the app (e.g. before first useGSAP); do not register inside a component that re-renders. useGSAP is a plugin that needs to be registered before use.\n\n## Scroll\n\n### ScrollToPlugin\n\nAnimates scroll position (window or a scrollable element). Use for “scroll to element” or “scroll to position” without ScrollTrigger.\n\n```javascript\ngsap.registerPlugin(ScrollToPlugin);\n\ngsap.to(window, { duration: 1, scrollTo: { y: 500 } });\ngsap.to(window, { duration: 1, scrollTo: { y: \"#section\", offsetY: 50 } });\ngsap.to(scrollContainer, { duration: 1, scrollTo: { x: \"max\" } });\n```\n\n**ScrollToPlugin — key config (scrollTo object):**\n\n| Option | Description |\n|--------|-------------|\n| `x`, `y` | Target scroll position (number), or `\"max\"` for maximum |\n| `element` | Selector or element to scroll to (for scroll-into-view) |\n| `offsetX`, `offsetY` | Offset in pixels from the target position |\n\n### ScrollSmoother\n\nSmooth scroll wrapper (smooths native scroll). Requires ScrollTrigger and a specific DOM structure (content wrapper + smooth wrapper). Use when smooth, momentum-style scroll is needed. See GSAP docs for setup; register after ScrollTrigger. DOM structure would look like: \n\n```html\n\u003Cbody>\n\t\u003Cdiv id=\"smooth-wrapper\">\n\t\t\u003Cdiv id=\"smooth-content\">\n\t\t\t\u003C!--- ALL YOUR CONTENT HERE --->\n\t\t\u003C\u002Fdiv>\n\t\u003C\u002Fdiv>\n\t\u003C!-- position: fixed elements can go outside --->\n\u003C\u002Fbody>\n```\n\n## DOM \u002F UI\n\n### Flip\n\nCapture state with `Flip.getState()`, then apply changes (e.g. layout or class changes), then use `Flip.from()` to animate from the previous state to the new state (FLIP: First, Last, Invert, Play). Use when animating between two layout states (lists, grids, expanded\u002Fcollapsed).\n\n```javascript\ngsap.registerPlugin(Flip);\n\nconst state = Flip.getState(\".item\");\n\u002F\u002F change DOM (reorder, add\u002Fremove, change classes)\nFlip.from(state, { duration: 0.5, ease: \"power2.inOut\" });\n```\n\n**Flip — key config (Flip.from vars):**\n\n| Option | Description |\n|--------|-------------|\n| `absolute` | Use `position: absolute` during the flip (default: `false`) |\n| `nested` | When true, only the first level of children is measured (better for nested transforms) |\n| `scale` | When true, scale elements to fit (avoids stretch); default `true` |\n| `simple` | When true, only position\u002Fscale are animated (faster, less accurate) |\n| `duration`, `ease` | Standard tween options |\n\n#### More information\n\nhttps:\u002F\u002Fgsap.com\u002Fdocs\u002Fv3\u002FPlugins\u002FFlip\n\n### Draggable\n\nMakes elements draggable, spinnable, or throwable with mouse\u002Ftouch. Use for sliders, cards, reorderable lists, or any drag interaction.\n\n```javascript\ngsap.registerPlugin(Draggable, InertiaPlugin);\n\nDraggable.create(\".box\", { type: \"x,y\", bounds: \"#container\", inertia: true });\nDraggable.create(\".knob\", { type: \"rotation\" });\n```\n\n**Draggable — key config options:**\n\n| Option | Description |\n|--------|-------------|\n| `type` | `\"x\"`, `\"y\"`, `\"x,y\"`, `\"rotation\"`, `\"scroll\"` |\n| `bounds` | Element, selector, or `{ minX, maxX, minY, maxY }` to constrain drag |\n| `inertia` | `true` to enable throw\u002Fmomentum (requires InertiaPlugin) |\n| `edgeResistance` | 0–1; resistance when dragging past bounds |\n| `cursor` | CSS cursor during drag |\n| `onDragStart`, `onDrag`, `onDragEnd` | Callbacks; receive event and target |\n| `onThrowUpdate`, `onThrowComplete` | Callbacks when inertia is active |\n\n### Inertia (InertiaPlugin)\n\nWorks with Draggable for momentum after release, or track the inertia\u002Fvelocity of any property of any object so that it can then seamlessly glide to a stop using a simple tween. Register with Draggable when using `inertia: true`:\n\n```javascript\ngsap.registerPlugin(Draggable, InertiaPlugin);\nDraggable.create(\".box\", { type: \"x,y\", inertia: true });\n```\n\nOr track velocity of a property: \n```javascript\nInertiaPlugin.track(\".box\", \"x\");\n```\n\nThen use `\"auto\"` to continue the current velocity and glide to a stop: \n\n```javascript\ngsap.to(obj, { inertia: { x: \"auto\" } });\n```\n\n### Observer\n\nNormalizes pointer and scroll input across devices. Use for swipe, scroll direction, or custom gesture logic without tying directly to scroll position like ScrollTrigger.\n\n```javascript\ngsap.registerPlugin(Observer);\n\nObserver.create({\n  target: \"#area\",\n  onUp: () => {},\n  onDown: () => {},\n  onLeft: () => {},\n  onRight: () => {},\n  tolerance: 10\n});\n```\n\n**Observer — key config options:**\n\n| Option | Description |\n|--------|-------------|\n| `target` | Element or selector to observe |\n| `onUp`, `onDown`, `onLeft`, `onRight` | Callbacks when swipe\u002Fscroll passes tolerance in that direction |\n| `tolerance` | Pixels before direction is detected; default 10 |\n| `type` | `\"touch\"`, `\"pointer\"`, or `\"wheel\"` (default: `\"touch,pointer\"`) |\n\n## Text\n\n### SplitText\n\nSplits an element’s text into characters, words, and\u002For lines (each in its own element) for staggered or per-unit animation. Use when animating text character-by-character, word-by-word, or line-by-line. Returns an instance with **chars**, **words**, **lines** (and **masks** when `mask` is set). Restore original markup with **revert()** or let **gsap.context()** revert. Integrates with **gsap.context()**, **matchMedia()**, and **useGSAP()**. API: **SplitText.create(target, vars)** (target = selector, element, or array).\n\n```javascript\ngsap.registerPlugin(SplitText);\n\nconst split = SplitText.create(\".heading\", { type: \"words, chars\" });\ngsap.from(split.chars, { opacity: 0, y: 20, stagger: 0.03, duration: 0.4 });\n\u002F\u002F later: split.revert() or let gsap.context() cleanup revert\n```\n\nWith **onSplit()** (v3.13.0+), animations run on each split and on re-split when **autoSplit** is used; returning a tween\u002Ftimeline from **onSplit()** lets SplitText clean up and sync progress on re-split:\n\n```javascript\nSplitText.create(\".split\", {\n  type: \"lines\",\n  autoSplit: true,\n  onSplit(self) {\n    return gsap.from(self.lines, { y: 100, opacity: 0, stagger: 0.05, duration: 0.5 });\n  }\n});\n```\n\n**SplitText — key config (SplitText.create vars):**\n\n| Option | Description |\n|--------|-------------|\n| **type** | Comma-separated: `\"chars\"`, `\"words\"`, `\"lines\"`. Default `\"chars,words,lines\"`. Only split what is needed (e.g. `\"words, chars\"` if not using lines) for performance. Avoid chars-only without words\u002Flines or use **smartWrap: true** to prevent odd line breaks. |\n| **charsClass**, **wordsClass**, **linesClass** | CSS class on each split element. Append `\"++\"` to add an incremented class (e.g. `linesClass: \"line++\"` → `line1`, `line2`, …). |\n| **aria** | `\"auto\"` (default), `\"hidden\"`, or `\"none\"`. Accessibility: `\"auto\"` adds `aria-label` on the split element and `aria-hidden` on line\u002Fword\u002Fchar elements so screen readers read the label; `\"hidden\"` hides all from readers; `\"none\"` leaves aria unchanged. Use `\"none\"` plus a screen-reader-only duplicate if nested links\u002Fsemantics must be exposed. |\n| **autoSplit** | When `true`, reverts and re-splits when fonts finish loading or when the element width changes (and lines are split), avoiding wrong line breaks. **Animations must be created inside onSplit()** so they target the newly split elements; **return** the animation from **onSplit()** for automatic cleanup and time-sync on re-split. |\n| **onSplit(self)** | Callback when split completes (and on each re-split if **autoSplit** is `true`). Receives the SplitText instance. Returning a GSAP tween or timeline enables automatic revert\u002Fsync of that animation when re-splitting. |\n| **mask** | `\"lines\"`, `\"words\"`, or `\"chars\"`. Wraps each unit in an extra element with `overflow: clip` for mask\u002Freveal effects. Only one type; access wrappers on the instance’s **masks** array (or use class `-mask` if a class is set). |\n| **tag** | Wrapper element tag; default `\"div\"`. Use `\"span\"` for inline (note: transforms like rotation\u002Fscale may not render on inline elements in some browsers). |\n| **deepSlice** | When `true` (default), nested elements (e.g. `\u003Cstrong>`) that span multiple lines are subdivided so lines don’t stretch vertically. Only applies when splitting lines. |\n| **ignore** | Selector or element(s) to leave unsplit (e.g. `ignore: \"sup\"`). |\n| **smartWrap** | When splitting **chars** only, wraps words in a `white-space: nowrap` span to avoid mid-word line breaks. Ignored if words or lines are split. Default `false`. |\n| **wordDelimiter** | Word boundary: string (default `\" \"`), RegExp, or `{ delimiter: RegExp, replaceWith: string }` for custom splitting (e.g. zero-width joiner for hashtags, or non-Latin). |\n| **prepareText(text, parent)** | Function that receives raw text and parent element; return modified text before splitting (e.g. to insert break markers for languages without spaces). |\n| **propIndex** | When `true`, adds a CSS variable with index on each split element (e.g. `--word: 1`, `--char: 2`). |\n| **reduceWhiteSpace** | Collapse consecutive spaces; default `true`. From v3.13.0 also honors line breaks and can insert `\u003Cbr>` for `\u003Cpre>`. |\n| **onRevert** | Callback when the instance is reverted. |\n\n**Tips:** Split only what is animated (e.g. skip chars if only animating words). For custom fonts, split after they load (e.g. `document.fonts.ready.then(...)`) or use **autoSplit: true** with **onSplit()**. To avoid kerning shift when splitting chars, use CSS `font-kerning: none; text-rendering: optimizeSpeed;`. Avoid `text-wrap: balance`; it can interfere with splitting. SplitText does not support SVG `\u003Ctext>`.\n\n**Learn more:** [SplitText](https:\u002F\u002Fgsap.com\u002Fdocs\u002Fv3\u002FPlugins\u002FSplitText\u002F)\n\n### ScrambleText\n\nAnimates text with a scramble\u002Fglitch effect. Use when revealing or transitioning text with a scramble.\n\n```javascript\ngsap.registerPlugin(ScrambleTextPlugin);\n\ngsap.to(\".text\", {\n  duration: 1,\n  scrambleText: { text: \"New message\", chars: \"01\", revealDelay: 0.5 }\n});\n```\n\n## SVG\n\n### DrawSVG (DrawSVGPlugin)\n\nReveals or hides the stroke of SVG elements by animating `stroke-dashoffset` \u002F `stroke-dasharray`. Works on `\u003Cpath>`, `\u003Cline>`, `\u003Cpolyline>`, `\u003Cpolygon>`, `\u003Crect>`, `\u003Cellipse>`. Use when “drawing” or “erasing” strokes.\n\n**drawSVG value:** Describes the **visible segment** of the stroke along the path (start and end positions), not “animate from A to B over time.” Format: `\"start end\"` in percent or length. Examples: `\"0% 100%\"` = full stroke; `\"20% 80%\"` = stroke only between 20% and 80% (gaps at both ends). The tween animates from the element’s **current** segment to the **target** segment — e.g. `gsap.to(\"#path\", { drawSVG: \"0% 100%\" })` goes from whatever it is now to full stroke. Single value (e.g. `0`, `\"100%\"`) means start is 0: `\"100%\"` is equivalent to `\"0% 100%\"`. \n\n**Required:** The element must have a visible stroke — set `stroke` and `stroke-width` in CSS or as SVG attributes; otherwise nothing is drawn.\n\n```javascript\ngsap.registerPlugin(DrawSVGPlugin);\n\n\u002F\u002F draw from nothing to full stroke\ngsap.from(\"#path\", { duration: 1, drawSVG: 0 });\n\u002F\u002F or explicit segment: from 0–0 to 0–100%\ngsap.fromTo(\"#path\", { drawSVG: \"0% 0%\" }, { drawSVG: \"0% 100%\", duration: 1 });\n\u002F\u002F stroke only in the middle (gaps at ends)\ngsap.to(\"#path\", { duration: 1, drawSVG: \"20% 80%\" });\n```\n\n**Caveats:** Only affects stroke (not fill). Prefer single-segment `\u003Cpath>` elements; multi-segment paths can render oddly in some browsers. Contents of `\u003Cuse>` cannot be visually changed. **DrawSVGPlugin.getLength(element)** and **DrawSVGPlugin.getPosition(element)** return stroke length and current position.\n\n**Learn more:** [DrawSVG](https:\u002F\u002Fgsap.com\u002Fdocs\u002Fv3\u002FPlugins\u002FDrawSVGPlugin)\n\n### MorphSVG (MorphSVGPlugin)\n\nMorphs one SVG shape into another by animating the `d` attribute (path data). Start and end shapes do not need the same number of points — MorphSVG converts to cubic beziers and adds points as needed. Use for icon-to-icon morphs, shape transitions, or path-based animations. Works on `\u003Cpath>`, `\u003Cpolyline>`, and `\u003Cpolygon>`; `\u003Ccircle>`, `\u003Crect>`, `\u003Cellipse>`, and `\u003Cline>` are converted internally or via **MorphSVGPlugin.convertToPath(selector | element)** (replaces the element in the DOM with a `\u003Cpath>`).\n\n**morphSVG value:** Can be a **selector** (e.g. `\"#lightning\"`), an **element**, **raw path data** (e.g. `\"M47.1,0.8 73.3,0.8...\"`), or for polygon\u002Fpolyline a **points string** (e.g. `\"240,220 240,70 70,70 70,220\"`). For full config use the **object form** with **shape** as the only required property.\n\n```javascript\ngsap.registerPlugin(MorphSVGPlugin);\n\n\u002F\u002F convert primitives to path first if needed:\nMorphSVGPlugin.convertToPath(\"circle, rect, ellipse, line\");\n\ngsap.to(\"#diamond\", { duration: 1, morphSVG: \"#lightning\", ease: \"power2.inOut\" });\n\u002F\u002F object form:\ngsap.to(\"#diamond\", {\n  duration: 1,\n  morphSVG: { shape: \"#lightning\", type: \"rotational\", shapeIndex: 2 }\n});\n\n```\n\n**MorphSVG — key config (morphSVG object):**\n\n| Option | Description |\n|--------|-------------|\n| **shape** | _(Required.)_ Target shape: selector, element, or raw path string. |\n| **type** | `\"linear\"` (default) or `\"rotational\"`. Rotational uses angle\u002Flength interpolation and can avoid kinks mid-morph; try it when linear looks wrong. |\n| **map** | How segments are matched: `\"size\"` (default), `\"position\"`, or `\"complexity\"`. Use when start\u002Fend segments don’t line up; if none work, split into multiple paths and morph each. |\n| **shapeIndex** | Offsets which point in the start path maps to the first point in the end path (avoids shape “crossing over” or inverting). Number for single-segment paths; **array** for multi-segment (e.g. `[5, 1, -8]`). Negative reverses that segment. Use **shapeIndex: \"log\"** once to log the auto-calculated value, then paste the number\u002Farray into the tween. **findShapeIndex(start, end)** (separate utility) provides an interactive UI to find a good value. Only applies to closed paths. |\n| **smooth** | (v3.14+). Adds smoothing points. Number (e.g. `80`), `\"auto\"`, or object: `{ points: 40 \\| \"auto\", redraw: true \\| false, persist: true \\| false }`. `redraw: false` keeps original anchors (perfect fidelity, less even spacing). `persist: false` removes added points when the tween ends. Use when the default morph looks jagged or unnatural. |\n| **curveMode** | Boolean (v3.14+). Interpolates control-handle angle\u002Flength instead of raw x\u002Fy to avoid kinks on curves. Try if a morph has a mid-morph kink. |\n| **origin** | Rotation origin for **type: \"rotational\"**. String: `\"50% 50%\"` (default) or `\"20% 60%, 35% 90%\"` for different start\u002Fend origins. |\n| **precision** | Decimal places for output path data; default `2`. |\n| **precompile** | Array of precomputed path strings (or use **precompile: \"log\"** once, copy from console). Skips expensive startup calculations; use for very complex morphs. Only for `\u003Cpath>` (convert polygon\u002Fpolyline first). |\n| **render** | Function(rawPath, target) called each update — e.g. draw to canvas. RawPath is an array of segments (each segment = array of alternating x,y cubic bezier coords). |\n| **updateTarget** | When using **render** (e.g. canvas-only), set **updateTarget: false** so the original `\u003Cpath>` is not updated. **MorphSVGPlugin.defaultUpdateTarget** sets default. |\n\n**Utilities:** **MorphSVGPlugin.convertToPath(selector | element)** converts circle\u002Frect\u002Fellipse\u002Fline\u002Fpolygon\u002Fpolyline to `\u003Cpath>` in the DOM. **MorphSVGPlugin.rawPathToString(rawPath)** and **stringToRawPath(d)** convert between path strings and raw arrays. The plugin stores the original `d` on the target (e.g. for tweening back: `morphSVG: \"#originalId\"` or the same element).\n\n**Tips:** For twisted or inverted morphs, set **shapeIndex** (use `\"log\"` or findShapeIndex()). For multi-segment paths, **shapeIndex** is an array (one value per segment). Precompile only when the first frame is slow; it does not fix jank during the tween (simplify the SVG or reduce size if needed).\n\n**Learn more:** [MorphSVG](https:\u002F\u002Fgsap.com\u002Fdocs\u002Fv3\u002FPlugins\u002FMorphSVGPlugin)\n\n### MotionPath (MotionPathPlugin)\n\nAnimates an element along an SVG path. Use when moving an object along a path (e.g. a curve or custom route).\n\n```javascript\ngsap.registerPlugin(MotionPathPlugin);\n\ngsap.to(\".dot\", {\n  duration: 2,\n  motionPath: { path: \"#path\", align: \"#path\", alignOrigin: [0.5, 0.5] }\n});\n```\n\n**MotionPath — key config (motionPath object):**\n\n| Option | Description |\n|--------|-------------|\n| `path` | SVG path element, selector, or path data string |\n| `align` | Path element or selector to align the target to |\n| `alignOrigin` | `[x, y]` origin (0–1); default `[0.5, 0.5]` |\n| `autoRotate` | Rotate element to follow path tangent |\n| `curviness` | 0–2; path smoothing |\n\n### MotionPathHelper\n\nVisual editor for MotionPath (alignment, offset). Use during development to tune path alignment.\n\n```javascript\ngsap.registerPlugin(MotionPathPlugin, MotionPathHelperPlugin);\n\nconst helper = MotionPathHelper.create(\".dot\", \"#path\", { end: 0.5 });\n\u002F\u002F adjust in UI, then use helper.path or helper.getProgress() in your animation\n```\n\n## Easing\n\n### CustomEase\n\nCustom easing curves (cubic-bezier or SVG path). Use when a built-in ease is not enough. Basic usage is covered in gsap-core; register when using:\n\n```javascript\ngsap.registerPlugin(CustomEase);\nconst ease = CustomEase.create(\"name\", \".17,.67,.83,.67\");\ngsap.to(\".el\", { x: 100, ease: ease, duration: 1 });\n```\n\n### EasePack\n\nAdds more named eases (e.g. SlowMo, RoughEase, ExpoScaleEase). Register and use the ease names in tweens.\n\n### CustomWiggle\n\nWiggle\u002Fshake easing. Use when a value should “wiggle” (multiple oscillations).\n\n### CustomBounce\n\nBounce-style easing with configurable strength.\n\n## Physics\n\n### Physics2D (Physics2DPlugin)\n\n2D physics (velocity, angle, gravity). Use when animating with simple physics (e.g. projectiles, bouncing).\n\n```javascript\ngsap.registerPlugin(Physics2DPlugin);\n\ngsap.to(\".ball\", {\n  duration: 2,\n  physics2D: {\n    velocity: 250,\n    angle: 80,\n    gravity: 500\n  }\n});\n```\n\n### PhysicsProps (PhysicsPropsPlugin)\n\nApplies physics to property values. Use for physics-driven property animation.\n\n```javascript\ngsap.registerPlugin(PhysicsPropsPlugin);\n\ngsap.to(\".obj\", {\n  duration: 2,\n  physicsProps: {\n    x: { velocity: 100, end: 300 },\n    y: { velocity: -50, acceleration: 200 }\n  }\n});\n```\n\n## Development\n\n### GSDevTools\n\nUI for scrubbing timelines, toggling animations, and debugging. Use during development only; do not ship. Register and create an instance with a timeline reference.\n\n```javascript\ngsap.registerPlugin(GSDevTools);\nGSDevTools.create({ animation: tl });\n```\n\n## Other\n\n### Pixi (PixiPlugin)\n\nIntegrates GSAP with PixiJS for animating Pixi display objects. Register when animating Pixi objects with GSAP.\n\n```javascript\ngsap.registerPlugin(PixiPlugin);\n\nconst sprite = new PIXI.Sprite(texture);\ngsap.to(sprite, { pixi: { x: 200, y: 100, scale: 1.5 }, duration: 1 });\n```\n\n## Best practices\n\n- ✅ Register every plugin used with **gsap.registerPlugin()** before first use.\n- ✅ Use **Flip.getState()** → DOM change → **Flip.from()** for layout transitions; use **Draggable** + **InertiaPlugin** for drag with momentum.\n- ✅ Revert plugin instances (e.g. `SplitTextInstance.revert()`) when components unmount or elements are removed.\n\n## Do Not\n\n- ❌ Use a plugin in a tween or API without registering it first (**gsap.registerPlugin()**).\n- ❌ Ship GSDevTools or development-only plugins to production.\n\n### Learn More\n\nhttps:\u002F\u002Fgsap.com\u002Fdocs\u002Fv3\u002FPlugins\u002F\n",{"name":5,"license":15,"description":7},"main","e4eeaefbd56226a251b228f1beb87a293ab3140a",".agents\u002Fskills\u002Fgsap-plugins",1,"https:\u002F\u002Fgithub.com\u002Fancyloce\u002Fanvilkit-studio","http:\u002F\u002Fanvilkit.aiiua.com\u002F","https:\u002F\u002Fgithub.com\u002Fancyloce","🧙‍♂️  Front-end mage with a secret back-end talent. \r\n🔮  Fluent in React, Vue, Go, and Rust.\r\n📚  Book addict and  🎵  music lover. Let's create some  ✨ magic","2026-11-01 10:38:50",{"source":8,"skillName":5,"commands":54,"prompt":64,"zipAvailable":65},[55,58,61],{"tool":56,"command":57},"npx","npx skills add ancyloce\u002Fanvilkit-studio --skill gsap-plugins",{"tool":59,"command":60},"bunx","bunx skills add ancyloce\u002Fanvilkit-studio --skill gsap-plugins",{"tool":62,"command":63},"pnpm","pnpm dlx skills add ancyloce\u002Fanvilkit-studio --skill gsap-plugins","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fancyloce\u002Fanvilkit-studio\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fgsap-plugins\u002FSKILL.md",true,[],[68,97,121,142,165,171],{"skillNo":69,"name":70,"nameZh":6,"description":71,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":72,"categories":73,"keywords":77,"publishedAt":39},"01M6JGVQAQWDRDT2CAQ2VH7D9V","react-doctor","Use when finishing a feature, fixing a bug, before committing React code, or when the user types `\u002Fdoctor`, asks to scan, triage, or clean up React diagnostics. Covers lint, accessibility, bundle size, architecture. Includes a regression check and a full local-triage workflow that fetches the canonical playbook.","https:\u002F\u002Fgithub.com\u002Fancyloce\u002Fanvilkit-studio\u002Fblob\u002Fmain\u002Fpackages\u002Fruntime\u002Fcore\u002F.claude\u002Fskills\u002Freact-doctor\u002FSKILL.md",[74],{"categoryNo":75,"categoryName":76,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发",[78,79,80,81,82,83,84,85,86,87,88,30,89,90,91,92,93,94,95,96],"react","doctor","react doctor","workflow","architecture","lint","code","playbook","regression","bundle size","check","regression check","up","clean","scan","local","feature","types","size",{"skillNo":98,"name":99,"nameZh":6,"description":100,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":101,"categories":102,"keywords":103,"publishedAt":39},"01M6JGVJ7H21GFHTJ57RBWC6C1","review-remediate","Use for the review-then-fix-everything loop on a package or diff — deliver a grounded review report immediately (no scoping questions, no plan-approval), then remediate every finding as tracked numbered items, gating each batch on typecheck\u002Flint\u002Ftest\u002Fbuild plus an independent Codex pass until clean. Trigger on \"review and fix \u003Cpackage>\", \"remediate the review\", \"review-fix-verify\", or when the user wants a deep review followed by autonomous remediation. Distinct from gstack `review` (pre-landing PR review) and `code-review` (diff only) — this reviews from scratch AND fixes to green.","https:\u002F\u002Fgithub.com\u002Fancyloce\u002Fanvilkit-studio\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Freview-remediate\u002FSKILL.md",[],[104,105,106,107,108,109,110,111,30,112,113,91,114,115,116,117,118,38,119,120],"review","review remediate","pr review","pr","package","autonomous","fix","landing","diff","only","green","deep","as","trigger","reviews","fixes","review report",{"skillNo":122,"name":123,"nameZh":6,"description":124,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":125,"categories":126,"keywords":127,"publishedAt":39},"01M6JGVDZD39P3Q97XGQPTTKQN","review-fixes","Close out an EXISTING code-review findings report — one subagent per finding, each with a regression test — then regenerate EN\u002Fzh\u002Fja\u002Fko docs, run all gates, and verify no stale or unexported symbols remain. Trigger on \"\u002Freview-fixes\", \"fix all N review findings\", \"close the review findings\", or when the user hands over a findings report to remediate. Distinct from review-remediate, which produces the review itself first — this skill starts from a report that already exists.","https:\u002F\u002Fgithub.com\u002Fancyloce\u002Fanvilkit-studio\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Freview-fixes\u002FSKILL.md",[],[104,119,128,129,84,130,131,132,133,86,110,134,135,136,137,117,138,139,140,141],"review fixes","test","code-review","first","verify","subagent","regression test","en","zh","gates","with","review findings","which","Existing",{"skillNo":143,"name":144,"nameZh":6,"description":145,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":146,"categories":147,"keywords":148,"publishedAt":39},"01M6JGV8J3ZRFGCXR2W3Q9ZWYK","release-prep","Pre-release verification + go\u002Fno-go checklist for publishing @anvilkit packages. Detects the release surface (main workspace fixed-group vs components submodule), runs changeset status and the check:all release gates, enforces this repo's publish gotchas (version.ts drift, ui peer ranges, never-publish canvas-templates, bundle\u002Fapi-snapshot), then hands the exact publish command to the user. Use when asked to \"prep a release\", \"check release readiness\", \"\u002Frelease-prep\", or before cutting a version. Does NOT commit or publish.","https:\u002F\u002Fgithub.com\u002Fancyloce\u002Fanvilkit-studio\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Frelease-prep\u002FSKILL.md",[],[149,150,151,152,153,154,155,156,157,158,30,159,160,161,162,44,163,137,164,38],"release","prep","release prep","canvas","components","commit","api","Bash","changeset","repo","version","never","publishing","peer","drift","true",{"skillNo":166,"name":70,"nameZh":6,"description":71,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":167,"categories":168,"keywords":170,"publishedAt":39},"01M6JGV33MBTTHMZ1SNJZHAA6H","https:\u002F\u002Fgithub.com\u002Fancyloce\u002Fanvilkit-studio\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Freact-doctor\u002FSKILL.md",[169],{"categoryNo":75,"categoryName":76,"taxonomyType":21,"taxonomyTypeDesc":22},[78,79,80,81,82,83,84,85,86,87,88,30,89,90,91,92,93,94,95,96],{"skillNo":172,"name":173,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":174,"categories":175,"keywords":179,"publishedAt":39},"01M6JGV1KQAFYH6ZQ864ZBCVVH","pre-refactor","https:\u002F\u002Fgithub.com\u002Fancyloce\u002Fanvilkit-studio\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fpre-refactor\u002FSKILL.md",[176],{"categoryNo":177,"categoryName":178,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000007","代码审查",[180,181],"refactor","pre refactor"]