动画 React:GreenSock 和 React Hooks
我的一位学生在 React 中实现 GSAP 动画时遇到了麻烦,受此启发,我决定进行一些实验并写下我学到的东西。
如果您不熟悉 useState,可以查看我的其他博客文章。
如果您不熟悉 GreenSock,可以查看我的博客文章。
我得说,我还在尝试这个,并学习 Hooks 和 GreenSock 的最佳实践。如果你对改进代码有什么建议,请在评论区留言!
这不是整个项目的完整教程,只是概述了我如何添加 GreenSock 并使用 Hooks 实现它。如果您只想查看代码,可以在下面查看👇
该项目使用样式化组件。如需了解更多信息,请查看此处的文档。
我为这个项目做的第一件事就是导入我将要使用的钩子。
import React, { useRef, useEffect, useState } from "react";
我还确保已将 GSAP 添加为依赖项并将其导入。
import { TweenMax, TimelineMax,Elastic, Back} from "gsap";
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);
我正在映射一堆数据来吐出我的卡片,因此在这个例子中我使用 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>
);
};
我在这里使用回调引用。
以下是Rodrigo撰写的 GreenSock 文档中关于 refs 的摘录:
请记住,ref 是一个回调函数,它在 JSX 代码中用作属性,用于获取标签返回的任何内容。但它是一个函数,现在你只是引用了该函数,而没有对它进行任何操作。你必须在构造函数中创建对 DOM 元素的引用,然后在渲染时使用回调函数来更新它。
对于我的函数式组件,我创建了对想要使用 useRef 进行动画处理的 DOM 元素的引用。然后,我在 JSX 中添加了回调 refs。
就像这样:
<RevealH3
ref={element => {
textRef = element;
}}
className="reveal"
>
现在我可以通过 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 }));
我们将在 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()
);
},[])
这是为了获取图片的引用。我将 .pause() 方法链接到了补间动画,这样它只会在我们设置事件时运行。
下面,我将动画添加到 onMouseEnter 事件中,并将 .play() 方法链接到该事件,这样当鼠标进入卡片时,动画就会运行。
<Card
onMouseEnter={() => mouseAnimation.play()}
className="dog-card "
key={props.id}
ref={element => {
cardRef = element;
}}>
第二动画
对于这个动画,我使用了 GreenSock 的 TimelineMax。我使用 useState Hook 设置了时间轴的初始状态。
const [tl] = useState(new TimelineMax({ paused: true }));
这会将初始状态设置为暂停。
然后我将动画添加到 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]);
注意,对于这个动画,我需要将状态添加到依赖数组中。由于我们将使用事件更新状态,因此在更新状态时,需要更新 useEffect 钩子。
这个动画引用了我隐藏的文本和卡片。动画开始时,文本会显示出来。然后卡片会缩小并消失。动画由“收养我”按钮上的 onClick 处理程序触发。
<DogButton
onClick={() => clickAnimation.play()}
>
在 onClick 事件中,我们将 clickAnimation 状态更新为播放,而不是其初始状态的暂停。
现在我们应该有两个可以运行的动画了。第一个动画在鼠标悬停在卡片上时触发,第二个动画在点击“领养我”按钮时触发。
鏂囩珷鏉ユ簮锛�https://dev.to/coffeecraftcode/animating-react-greensock-and-react-hooks-55o4