[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6J2WHWD3NJKT0119RBP8J45":3,"skill-same:01M6J2WHWD3NJKT0119RBP8J45":70},{"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":26,"publishedAt":42,"linkOnly":43,"markdownContent":44,"markdownBody":45,"frontMatter":46,"refName":47,"commitSha":48,"skillDirectory":49,"forksCount":50,"repositoryHtmlUrl":51,"repositoryHomepageUrl":52,"ownerHtmlUrl":53,"ownerBlogUrl":6,"ownerDescription":54,"updatedAt":55,"install":56,"resources":69},"01M6J2WHWD3NJKT0119RBP8J45","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.","onfire7777\u002Funiversal-ai-skills-library","onfire7777","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F240866566?v=4","01M6HZJCF2QEW8DY7HE1JDKGK2","Jake B","01M6HZJCKK8SEXKN76V9SGKZ12",17,"MIT","https:\u002F\u002Fgithub.com\u002Fonfire7777\u002Funiversal-ai-skills-library\u002Fblob\u002Fmain\u002Fskills\u002Fgsap-plugins\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000016","图像与多媒体","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[27,28,29,30,31,32,33,34,35,36,37,38,39,40,41],"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\nlicense: MIT\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.\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## 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## 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","2a76bf42ce5c860adf8d0f3fcf3f6804109ac65b","skills\u002Fgsap-plugins",0,"https:\u002F\u002Fgithub.com\u002Fonfire7777\u002Funiversal-ai-skills-library","https:\u002F\u002Fgithub.com\u002Fonfire7777\u002Funiversal-ai-skills-library\u002Fwiki","https:\u002F\u002Fgithub.com\u002Fonfire7777","trying to create something actually useful","2026-09-11 13:25:26",{"source":8,"skillName":5,"commands":57,"prompt":67,"zipAvailable":68},[58,61,64],{"tool":59,"command":60},"npx","npx skills add onfire7777\u002Funiversal-ai-skills-library --skill gsap-plugins",{"tool":62,"command":63},"bunx","bunx skills add onfire7777\u002Funiversal-ai-skills-library --skill gsap-plugins",{"tool":65,"command":66},"pnpm","pnpm dlx skills add onfire7777\u002Funiversal-ai-skills-library --skill gsap-plugins","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fonfire7777\u002Funiversal-ai-skills-library\u002Fblob\u002Fmain\u002Fskills\u002Fgsap-plugins\u002FSKILL.md",true,[],[71,98,107,117,137,153],{"skillNo":72,"name":73,"nameZh":6,"description":74,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":75,"categories":76,"keywords":86,"publishedAt":42},"01M6J7MPK8ZE9VQJR4KK6ZMZJQ","zyte-api-automation","Automate Zyte API tasks via Rube MCP (Composio). Always search tools first for current schemas.","https:\u002F\u002Fgithub.com\u002Fonfire7777\u002Funiversal-ai-skills-library\u002Fblob\u002Fmain\u002Fskills\u002Fzyte-api-automation\u002FSKILL.md",[77,80,81],{"categoryNo":78,"categoryName":79,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":82,"categoryName":83,"taxonomyType":84,"taxonomyTypeDesc":85},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[87,88,89,41,90,91,92,93,94,95,96,97],"automation","api","zyte api automation","MCP","via","composio","automate","first","Tools","search","tasks",{"skillNo":99,"name":100,"nameZh":6,"description":101,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":102,"categories":103,"keywords":105,"publishedAt":42},"01M6J7MK45K2ZZ0EWTP1N6SZ7F","zylvie-automation","Automate Zylvie tasks via Rube MCP (Composio). Always search tools first for current schemas.","https:\u002F\u002Fgithub.com\u002Fonfire7777\u002Funiversal-ai-skills-library\u002Fblob\u002Fmain\u002Fskills\u002Fzylvie-automation\u002FSKILL.md",[104],{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},[87,106,90,97,41,91,92,93,94,95,96],"zylvie automation",{"skillNo":108,"name":109,"nameZh":6,"description":110,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":111,"categories":112,"keywords":114,"publishedAt":42},"01M6J7MGDCVZ3YCXGEAPR5V3MS","zoominfo-automation","Automate Zoominfo tasks via Rube MCP (Composio). Always search tools first for current schemas.","https:\u002F\u002Fgithub.com\u002Fonfire7777\u002Funiversal-ai-skills-library\u002Fblob\u002Fmain\u002Fskills\u002Fzoominfo-automation\u002FSKILL.md",[113],{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},[115,116,87,93,92,95,91,97,41,96,90,94],"zoominfo","zoominfo automation",{"skillNo":118,"name":119,"nameZh":6,"description":120,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":121,"categories":122,"keywords":124,"publishedAt":42},"01M6J7MDK48N09B7C6H9TCPY9A","zoom-out","Tell the agent to zoom out and give broader context or a higher-level perspective. Use when you're unfamiliar with a section of code or need to understand how it fits into the bigger picture.","https:\u002F\u002Fgithub.com\u002Fonfire7777\u002Funiversal-ai-skills-library\u002Fblob\u002Fmain\u002Fskills\u002Fzoom-out\u002FSKILL.md",[123],{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},[125,126,127,128,129,130,131,132,133,33,134,135,136],"zoom out","zoom","perspective","code","re","you","with","understand","it","context","how","true",{"skillNo":138,"name":139,"nameZh":6,"description":140,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":141,"categories":142,"keywords":144,"publishedAt":42},"01M6J7MAD47Q9A55AK3EXS914H","zoho-mail-automation","Automate Zoho Mail tasks via Rube MCP (Composio): email sending, folders, labels, and mailbox management. Always search tools first for current schemas.","https:\u002F\u002Fgithub.com\u002Fonfire7777\u002Funiversal-ai-skills-library\u002Fblob\u002Fmain\u002Fskills\u002Fzoho-mail-automation\u002FSKILL.md",[143],{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},[145,146,147,148,87,149,96,97,90,95,150,41,91,151,92,152,93,94],"mail","zoho mail automation","zoho","zoho mail","email","labels","mailbox","management",{"skillNo":154,"name":155,"nameZh":6,"description":156,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":157,"categories":158,"keywords":163,"publishedAt":42},"01M6J7M76HTWPP8TRWWCDS42NB","zoho-invoice-automation","Automate Zoho Invoice tasks via Rube MCP (Composio): invoices, estimates, expenses, clients, and payment tracking. Always search tools first for current schemas.","https:\u002F\u002Fgithub.com\u002Fonfire7777\u002Funiversal-ai-skills-library\u002Fblob\u002Fmain\u002Fskills\u002Fzoho-invoice-automation\u002FSKILL.md",[159,160],{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":161,"categoryName":162,"taxonomyType":84,"taxonomyTypeDesc":85},"01JBSEEDCAT000000000000040","财务",[164,87,147,165,90,91,41,166,167,168,92,169,170,93,94,95,96,97],"invoice","zoho invoice automation","invoices","expenses","clients","payment","tracking"]