动画 React:GreenSock 和 React Hooks

2025-06-08

动画 React:GreenSock 和 React Hooks

我的一位学生在 React 中实现 GSAP 动画时遇到了麻烦,受此启发,我决定进行一些实验并写下我学到的东西。

如果您不熟悉 useState,可以查看我的其他博客文章
如果您不熟悉 GreenSock,可以查看我的博客文章

我得说,我还在尝试这个,并学习 Hooks 和 GreenSock 的最佳实践。如果你对改进代码有什么建议,请在评论区留言!

这不是整个项目的完整教程,只是概述了我如何添加 GreenSock 并使用 Hooks 实现它。如果您只想查看代码,可以在下面查看👇

代码

该项目使用样式化组件。如需了解更多信息,请查看此处的文档。

我为这个项目做的第一件事就是导入我将要使用的钩子。

import React, { useRef, useEffect, useState } from "react"; 
Enter fullscreen mode Exit fullscreen mode

我还确保已将 GSAP 添加为依赖项并将其导入。

import { TweenMax, TimelineMax,Elastic, Back} from "gsap";
Enter fullscreen mode Exit fullscreen mode

TweenMax、TimeLineMax、Elastic 和 Back 都是我在动画中使用的 GreenSock 的一部分,因此我需要导入每个模块。

TweenMax 和 TimeLineMax 用于创建动画。Elastic
和 Back 是我在动画中使用的缓动类型。
这些功能很快会在新的 GSAP v3 中得到改进。我会在新的 GreenSock API 上线后更新这篇文章,但即使如此,您仍然可以使用我在 GSAP v3 中使用的现有语法。

如果你想了解更多缓动效果,我强烈建议你在创建动画时查看这个缓动可视化工具。
缓动可视化工具

useRef

useRef 钩子主要用于访问 DOM,但它的作用远不止于此。它是一个可变对象,可以在多次重新渲染后持久化一个值。它与 useState 钩子非常相似,只不过你可以通过它的 .current 属性读写它的值,并且更改它的值不会重新渲染组件。Hunor
Márton Borbély CSS-Tricks

在 React 中使用 GreenSock 实现动画的关键在于确保获取到想要动画的元素的引用。我们可以使用 useRef hook 来获取想要动画的元素的引用。

对于我们的卡片,我们将为图像、一些隐藏的文本以及实际的卡片添加动画。我设置了如下引用:

  let imgRef = useRef(null);
  let textRef = useRef(null);
  let cardRef = useRef(null);
Enter fullscreen mode Exit fullscreen mode

我正在映射一堆数据来吐出我的卡片,因此在这个例子中我使用 let 而不是 const,因为 img、文本和卡片引用将根据卡片而改变。

接下来我需要添加对组件的引用。

    <Card
      onMouseEnter={() => mouseAnimation.play()}
      className="dog-card "
      key={props.id}
      ref={element => {
        cardRef = element;
      }}>
      <DogImage
        ref={element => {
          imgRef = element;
        }}
        className="dog-image"
        alt="random dog"
        src={props.imgUrl}
      />
      <RevealH3
        ref={element => {
          textRef = element;
        }}
        className="reveal"
      >

        Thank you! 
       <span role="img" aria-label="triple pink heart">💗</span>
      </RevealH3>
      <DogButton
        onClick={() => clickAnimation.play()}
      >
        AdoptMe
      </DogButton>
      <MainTitle>{props.breed}</MainTitle>
    </Card>
  );
};
Enter fullscreen mode Exit fullscreen mode

我在这里使用回调引用。

以下是Rodrigo撰写的 GreenSock 文档中关于 refs 的摘录

请记住,ref 是一个回调函数,它在 JSX 代码中用作属性,用于获取标签返回的任何内容。但它是一个函数,现在你只是引用了该函数,而没有对它进行任何操作。你必须在构造函数中创建对 DOM 元素的引用,然后在渲染时使用回调函数来更新它。

对于我的函数式组件,我创建了对想要使用 useRef 进行动画处理的 DOM 元素的引用。然后,我在 JSX 中添加了回调 refs。
就像这样:

      <RevealH3
        ref={element => {
          textRef = element;
        }}
        className="reveal"
      >
Enter fullscreen mode Exit fullscreen mode

现在我可以通过 useRef hook 访问 DOM 元素,我可以像在 GreenSock 中一样为这些元素添加动画效果。唯一的区别是,我将动画放在 useEffect hook 中,并在 useState hook 中设置初始动画状态。

每当组件中有需要更新的数据时,我们都会使用 useState。在这个应用中,我更新了几个动画,所以我将它们添加到了 state 中。

建立我们的国家

  const [mouseAnimation, setMouseAnimation] = useState();
  const [clickAnimation, setClickAnimation] = useState();
  const [tl] = useState(new TimelineMax({ paused: true }));
Enter fullscreen mode Exit fullscreen mode

我们将在 useEffect 钩子中设置 setMouseAnimation 和 setClickAnimation。它们将通过 JSX 中的事件进行更新。

根据 React 文档,我将动画拆分到不同的 useEffect hooks 中,而不是一个。据我所知,这应该是最佳实践。

第一个动画

useEffect(() => {
    setMouseAnimation(
      TweenMax.to(imgRef, 1, {
        scale: 1,
        filter: "none",
        ease: Elastic.easeOut.config(1, 0.75)
      }).pause()
    );
  },[])
Enter fullscreen mode Exit fullscreen mode

这是为了获取图片的引用。我将 .pause() 方法链接到了补间动画,这样它只会在我们设置事件时运行。
下面,我将动画添加到 onMouseEnter 事件中,并将 .play() 方法链接到该事件,这样当鼠标进入卡片时,动画就会运行。

    <Card
      onMouseEnter={() => mouseAnimation.play()}
      className="dog-card "
      key={props.id}
      ref={element => {
        cardRef = element;
      }}>
Enter fullscreen mode Exit fullscreen mode

第二动画

对于这个动画,我使用了 GreenSock 的 TimelineMax。我使用 useState Hook 设置了时间轴的初始状态。

const [tl] = useState(new TimelineMax({ paused: true }));
Enter fullscreen mode Exit fullscreen mode

这会将初始状态设置为暂停。

然后我将动画添加到 useEffect 钩子中。

useEffect(() => {
    setClickAnimation( . // here we are set are state to the timeline
      tl.add("s"),
      tl
        .to(
          textRef,
          1,
          {
            autoAlpha: 1,
            y: 0,
            ease: Elastic.easeIn.config(1, 0.75)
          },
          "s"
        )
        .to(
          cardRef,
          0.4,
          {
            transformOrigin: "center center",
            ease: Back.easeIn.config(1.4),
            scale: 0.1
          },
          "s+=1.5"
        )
        .to(
          cardRef,
          0.4,
          {
            opacity: 0,
            display: "none"
          },
          "s+=2"
        )
    );
  }, [tl]);
Enter fullscreen mode Exit fullscreen mode

注意,对于这个动画,我需要将状态添加到依赖数组中。由于我们将使用事件更新状态,因此在更新状态时,需要更新 useEffect 钩子。

这个动画引用了我隐藏的文本和卡片。动画开始时,文本会显示出来。然后卡片会缩小并消失。动画由“收养我”按钮上的 onClick 处理程序触发。

      <DogButton
        onClick={() => clickAnimation.play()}
      >
Enter fullscreen mode Exit fullscreen mode

在 onClick 事件中,我们将 clickAnimation 状态更新为播放,而不是其初始状态的暂停。

现在我们应该有两个可以运行的动画了。第一个动画在鼠标悬停在卡片上时触发,第二个动画在点击“领养我”按钮时触发。

鏂囩珷鏉ユ簮锛�https://dev.to/coffeecraftcode/animating-react-greensock-and-react-hooks-55o4
PREV
AWS 云从业者考试:云概念 云概念
NEXT
10 个很棒的世界政府设计系统和 UI 工具包