Gsap React

Bbeierle12/Skill-MCP-Claude/skills/gsap-react

作者 Bbeierle12c35d6c509b8f0331697f19ef45b651f274deba60无许可证8 个星标收录于 2026年10月9日更新于 2026年10月9日仓库今天更新

GSAP integration with React including useGSAP hook, ref handling, cleanup patterns, and context management. Use when implementing GSAP animations in React components, handling component lifecycle, or building reusable animation hooks.

AI 生成的概览

在 React 中集成 GSAP 动画的专用模式,涵盖 useGSAP、ref、清理与上下文管理。

功能
该技能提供在 React 中使用 GSAP 动画的针对性指导,核心是 useGSAP hook 与 gsap.context。内容涵盖 ref 用法、自动与手动清理、使用 contextSafe 的事件处理、时间线管理、ScrollTrigger 用法以及自定义动画 hook。产出的是代码模式与示例,而非文件或脚本。
适用场景
适用于在 React 组件中实现 GSAP 动画、处理组件生命周期与清理,或构建可复用动画 hook 的场景。也适合将 ScrollTrigger 或事件驱动动画接入 React 时使用。
运行要求
需要在 React 项目中安装 gsap 与 @gsap/react npm 包;仅为说明性内容,不附带脚本。

GSAP React Integration

React-specific patterns for GSAP animations.

Quick Start

bash
npm install gsap @gsap/react
tsx
import { useGSAP } from '@gsap/react';import gsap from 'gsap';
function Component() {  const containerRef = useRef(null);
  useGSAP(() => {    gsap.to('.box', { x: 200, duration: 1 });  }, { scope: containerRef });
  return (    <div ref={containerRef}>      <div className="box">Animated</div>    </div>  );}

useGSAP Hook

Basic Usage

tsx
import { useGSAP } from '@gsap/react';import gsap from 'gsap';
function AnimatedComponent() {  const container = useRef(null);
  useGSAP(() => {    // All GSAP animations here    gsap.from('.item', {      opacity: 0,      y: 50,      stagger: 0.1    });  }, { scope: container }); // Scope limits selector queries
  return (    <div ref={container}>      <div className="item">Item 1</div>      <div className="item">Item 2</div>      <div className="item">Item 3</div>    </div>  );}

With Dependencies

tsx
function AnimatedComponent({ isOpen }) {  const container = useRef(null);
  useGSAP(() => {    gsap.to('.drawer', {      height: isOpen ? 'auto' : 0,      duration: 0.3    });  }, { scope: container, dependencies: [isOpen] });
  return (    <div ref={container}>      <div className="drawer">Content</div>    </div>  );}

Returning Context

tsx
function Component() {  const container = useRef(null);
  const { context, contextSafe } = useGSAP(() => {    gsap.to('.box', { x: 200 });  }, { scope: container });
  // Use contextSafe for event handlers  const handleClick = contextSafe(() => {    gsap.to('.box', { rotation: 360 });  });
  return (    <div ref={container}>      <div className="box" onClick={handleClick}>Click me</div>    </div>  );}

Ref Patterns

Single Element Ref

tsx
function SingleElement() {  const boxRef = useRef(null);
  useGSAP(() => {    gsap.to(boxRef.current, {      x: 200,      rotation: 360,      duration: 1    });  });
  return <div ref={boxRef}>Box</div>;}

Multiple Element Refs

tsx
function MultipleElements() {  const itemsRef = useRef([]);
  useGSAP(() => {    gsap.from(itemsRef.current, {      opacity: 0,      y: 30,      stagger: 0.1    });  });
  return (    <div>      {[1, 2, 3].map((item, i) => (        <div          key={item}          ref={el => itemsRef.current[i] = el}        >          Item {item}        </div>      ))}    </div>  );}

Dynamic Refs

tsx
function DynamicList({ items }) {  const itemsRef = useRef(new Map());
  useGSAP(() => {    gsap.from(Array.from(itemsRef.current.values()), {      opacity: 0,      y: 20,      stagger: 0.05    });  }, { dependencies: [items.length] });
  return (    <div>      {items.map(item => (        <div          key={item.id}          ref={el => {            if (el) itemsRef.current.set(item.id, el);            else itemsRef.current.delete(item.id);          }}        >          {item.name}        </div>      ))}    </div>  );}

Context and Cleanup

Automatic Cleanup

tsx
// useGSAP automatically cleans up animations on unmountfunction Component() {  useGSAP(() => {    // This timeline is automatically killed on unmount    gsap.timeline()      .to('.a', { x: 100 })      .to('.b', { x: 100 });  });}

Manual Context (Without useGSAP)

tsx
import gsap from 'gsap';
function Component() {  useEffect(() => {    const ctx = gsap.context(() => {      gsap.to('.box', { x: 200 });      gsap.to('.circle', { rotation: 360 });    });
    return () => ctx.revert(); // Cleanup  }, []);}

Scoped Context

tsx
function Component() {  const containerRef = useRef(null);
  useEffect(() => {    const ctx = gsap.context(() => {      // Selectors only query within containerRef      gsap.to('.item', { opacity: 1 });    }, containerRef);
    return () => ctx.revert();  }, []);}

Event Handlers

contextSafe for Events

tsx
function InteractiveComponent() {  const container = useRef(null);
  const { contextSafe } = useGSAP(() => {    // Initial animation    gsap.set('.box', { scale: 1 });  }, { scope: container });
  const handleMouseEnter = contextSafe(() => {    gsap.to('.box', { scale: 1.1, duration: 0.2 });  });
  const handleMouseLeave = contextSafe(() => {    gsap.to('.box', { scale: 1, duration: 0.2 });  });
  return (    <div ref={container}>      <div        className="box"        onMouseEnter={handleMouseEnter}        onMouseLeave={handleMouseLeave}      >        Hover me      </div>    </div>  );}

useCallback Alternative

tsx
function Component() {  const boxRef = useRef(null);  const tweenRef = useRef(null);
  const animateBox = useCallback(() => {    tweenRef.current?.kill();    tweenRef.current = gsap.to(boxRef.current, {      x: '+=50',      duration: 0.3    });  }, []);
  useEffect(() => {    return () => tweenRef.current?.kill();  }, []);
  return <div ref={boxRef} onClick={animateBox}>Click</div>;}

Timeline Management

Timeline Ref Pattern

tsx
function TimelineComponent() {  const container = useRef(null);  const tl = useRef(null);
  useGSAP(() => {    tl.current = gsap.timeline({ paused: true })      .to('.box', { x: 200 })      .to('.box', { y: 100 })      .to('.box', { rotation: 360 });  }, { scope: container });
  const play = () => tl.current?.play();  const reverse = () => tl.current?.reverse();  const restart = () => tl.current?.restart();
  return (    <div ref={container}>      <div className="box">Animated</div>      <button onClick={play}>Play</button>      <button onClick={reverse}>Reverse</button>      <button onClick={restart}>Restart</button>    </div>  );}

Controlled Timeline

tsx
function ControlledAnimation({ progress }) {  const container = useRef(null);  const tl = useRef(null);
  useGSAP(() => {    tl.current = gsap.timeline({ paused: true })      .to('.element', { x: 500 })      .to('.element', { y: 200 });  }, { scope: container });
  // Update timeline progress when prop changes  useEffect(() => {    if (tl.current) {      tl.current.progress(progress);    }  }, [progress]);
  return (    <div ref={container}>      <div className="element">Controlled</div>    </div>  );}

ScrollTrigger in React

Basic ScrollTrigger

tsx
import { useGSAP } from '@gsap/react';import gsap from 'gsap';import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
function ScrollComponent() {  const container = useRef(null);
  useGSAP(() => {    gsap.from('.section', {      opacity: 0,      y: 100,      scrollTrigger: {        trigger: '.section',        start: 'top 80%',        toggleActions: 'play none none none'      }    });  }, { scope: container });
  return (    <div ref={container}>      <div className="section">Scroll to reveal</div>    </div>  );}

ScrollTrigger Cleanup

tsx
function ScrollComponent() {  const container = useRef(null);
  useGSAP(() => {    const triggers = [];
    gsap.utils.toArray('.item').forEach(item => {      const trigger = ScrollTrigger.create({        trigger: item,        start: 'top 80%',        onEnter: () => gsap.to(item, { opacity: 1 })      });      triggers.push(trigger);    });
    // Return cleanup function    return () => triggers.forEach(t => t.kill());  }, { scope: container });}

Custom Hooks

useAnimation Hook

tsx
function useAnimation(animation, deps = []) {  const elementRef = useRef(null);  const tweenRef = useRef(null);
  useGSAP(() => {    if (elementRef.current) {      tweenRef.current = animation(elementRef.current);    }    return () => tweenRef.current?.kill();  }, { dependencies: deps });
  return elementRef;}
// Usagefunction Component() {  const boxRef = useAnimation((el) =>    gsap.from(el, { opacity: 0, y: 50, duration: 0.5 })  );
  return <div ref={boxRef}>Animated</div>;}

useFadeIn Hook

tsx
function useFadeIn(options = {}) {  const { duration = 0.5, delay = 0, y = 30 } = options;  const ref = useRef(null);
  useGSAP(() => {    gsap.from(ref.current, {      opacity: 0,      y,      duration,      delay,      ease: 'power2.out'    });  });
  return ref;}
// Usagefunction Card() {  const cardRef = useFadeIn({ delay: 0.2 });  return <div ref={cardRef}>Card content</div>;}

useHoverAnimation Hook

tsx
function useHoverAnimation(enterAnimation, leaveAnimation) {  const ref = useRef(null);  const { contextSafe } = useGSAP({ scope: ref });
  const onEnter = contextSafe(() => enterAnimation(ref.current));  const onLeave = contextSafe(() => leaveAnimation(ref.current));
  return { ref, onMouseEnter: onEnter, onMouseLeave: onLeave };}
// Usagefunction Button() {  const hoverProps = useHoverAnimation(    (el) => gsap.to(el, { scale: 1.05, duration: 0.2 }),    (el) => gsap.to(el, { scale: 1, duration: 0.2 })  );
  return <button {...hoverProps}>Hover me</button>;}

Temporal Collapse Patterns

Animated Countdown Digit

tsx
function CountdownDigit({ value, label }) {  const digitRef = useRef(null);  const prevValue = useRef(value);
  useGSAP(() => {    if (prevValue.current !== value) {      gsap.timeline()        .to(digitRef.current, {          rotationX: -90,          opacity: 0,          duration: 0.25,          ease: 'power2.in'        })        .call(() => {          digitRef.current.textContent = value;          prevValue.current = value;        })        .fromTo(digitRef.current,          { rotationX: 90, opacity: 0 },          { rotationX: 0, opacity: 1, duration: 0.25, ease: 'power2.out' }        );    }  }, { dependencies: [value] });
  return (    <div className="digit-container">      <span ref={digitRef} className="digit">{value}</span>      <span className="label">{label}</span>    </div>  );}

Cosmic Pulse Effect

tsx
function CosmicPulse({ children, color = '#00F5FF' }) {  const containerRef = useRef(null);
  useGSAP(() => {    gsap.to(containerRef.current, {      boxShadow: `0 0 30px ${color}, 0 0 60px ${color}`,      duration: 1,      repeat: -1,      yoyo: true,      ease: 'sine.inOut'    });  }, { scope: containerRef });
  return <div ref={containerRef}>{children}</div>;}

Performance Tips

tsx
// 1. Use will-change for heavy animationsgsap.set('.animated', { willChange: 'transform' });
// 2. Batch similar animationsuseGSAP(() => {  gsap.to('.item', { opacity: 1, stagger: 0.1 }); // Single tween  // Not: items.forEach(item => gsap.to(item, ...)) // Multiple tweens});
// 3. Use refs over selectors for frequently animated elementsconst boxRef = useRef(null);gsap.to(boxRef.current, { x: 100 }); // Faster
// 4. Kill animations on rapid state changesconst tweenRef = useRef(null);useEffect(() => {  tweenRef.current?.kill();  tweenRef.current = gsap.to(...);}, [dependency]);

Reference

  • See gsap-fundamentals for animation basics
  • See gsap-sequencing for timeline composition
  • See gsap-scrolltrigger for scroll-based animations

来源与署名

来源:Bbeierle12/Skill-MCP-Claude位于skills/gsap-react提交c35d6c5

许可证: 无许可证

内容归原作者所有。SourceWeft 从公开仓库中收录这些内容。

举报或申请下架