英文:
Simple animation with react and gsap
问题
以下是代码部分的翻译:
import React, { useRef, useEffect } from 'react';
import { gsap } from 'gsap';
const tl = gsap.timeline({paused: true, repeat: 0});
function App() {
const waitingAnimationRef = useRef(null);
useEffect(() => {
tl.set(waitingAnimationRef, {autoAlpha: 0});
tl.play();
}, []);
return (
<div className="App">
<div id="red-circle" ref={waitingAnimationRef}></div>
</div>
);
}
export default App;
如果您需要更多帮助或有其他问题,请随时提出。
英文:
I have been struggling with this for days and am just having trouble wrapping my head around the varied documentation and examples out there, so I'm hoping someone can help with the specific example I'm working on and that might in turn help me understand this a bit better. This seems like it's far more convoluted than it should be, but I'm sure that's just my lack of understanding of how it all works.
The goal: make a very simple animation of a red ball moving back and forth. Hell, make any kind of animation at all (once I can confirm that gsap is animating anything at all, I should be able to take it from there).
The problem: Nothing happens. No errors, nothing clear to go on, just nothing. I still don't have a strong understanding of how this should work; a lot of the guides I've checked seem to be using different methods and don't go into a lot of detail as to why, so it's made it difficult to extend that knowledge to my specific scenario.
The code. I've simplified this greatly because, as I mentioned, all I really need is to just get any kind of gsap animation to work at all and from there I'm confident I can do with it what I need. If anyone feels like it would make a difference, I'm happy to update with the full code:
import React, { useRef, useEffect } from 'react';
import { gsap } from 'gsap';
const tl = gsap.timeline({paused: true, repeat: 0});
function App() {
const waitingAnimationRef = useRef(null);
useEffect(() => {
tl.set(waitingAnimationRef, {autoAlpha: 0});
tl.play();
}, []);
return (
<div className="App">
<div id="red-circle" ref={waitingAnimationRef}></div>
</div>
);
}
export default App;
答案1
得分: 3
以下是翻译好的部分:
- 使用
gsap.timeline
创建一个时间轴,并将其存储在引用中。可以像您以前那样创建时间轴,将其创建在组件外部,然后将时间轴传递给组件中的引用。在这个示例中,我通过直接将时间轴的变量名传递给useRef
钩子来实现:const tl = useRef(timeline);
。 - 我使用了时间轴选项
{ repeat: -1, yoyo: true }
,以使动画在交替方向中无限循环,因为您说您想制作一个“来回移动”的球。 - 您还需要一个 DOM 节点引用,以便将其传递给
gsap.context()
方法。在组件中创建引用并将其传递给组件的包装元素。在这里,我将其命名为app
(const app = useRef(null)
),然后将其传递给 App 组件的顶级 div 元素,使用ref={app}
。确保将引用传递给 DOM 节点,而不是 React 组件(否则您将不得不将引用传递到其中的节点子元素)。为什么使用引用?因为引用在组件重新渲染时与状态一样稳定,但与状态不同,修改引用不会导致重新渲染。useRef
钩子返回一个具有名为current
的属性的对象。在引用中放入的任何内容都可以通过current
属性访问。 - 使用
useLayoutEffect()
钩子而不是useEffect
。React 保证useLayoutEffect
中的代码以及其中安排的任何状态更新将在浏览器重新绘制屏幕之前处理。useEffect
钩子不会阻止浏览器重新绘制。大多数情况下,我们希望这样,以确保我们的应用程序不会减慢速度。但是,在这种情况下,useLayoutEffect
钩子确保 React 执行了所有 DOM 修改,元素对 gsap 可以进行动画处理。 - 在
useLayoutEffect
内部,您将使用 gsap 上下文方法。gsap 上下文方法接受两个参数:回调函数和组件引用。回调函数是您可以访问时间轴的地方(不要忘记通过引用对象的current
属性进行访问),并运行动画。 - 有两种方式来定位您要在时间轴上进行动画处理的元素:要么使用引用来存储 DOM 节点,要么使用选择器。我使用了
".box"
类的选择器来选择方框元素。这很简单,而且很好,因为它只会选择当前组件的子元素中匹配的元素。我为圆圈组件使用了引用。我包含了这个示例,这样您可以看到如何使用 forwardRefs 将引用从 App 组件通过 Circle 组件传递给子 div DOM 节点。尽管这更像是一种“React风格”的方法,但如果要动画处理很多元素,它会更加复杂和不够灵活。 - 就像
useEffect
一样,useLayoutEffect
返回一个清理函数。方便的是,gsap 上下文对象具有一个名为revert
的清理方法。
请注意,由于您要求只翻译代码部分,因此上述内容是代码的翻译。
英文:
Here's a working example and some tips to help you make some sense of it:
- Create a timeline with
gasp.timeline
and store it in a ref. You can do this as you've done, creating the timeline outside your component, but then you need to pass that timeline to the ref in your component. In this example, I did that by passing the variable name for the timeline to the useRef hook directly:const tl = useRef(timeline);
. - I used the timeline options
{ repeat: -1, yoyo: true }
so that the animation would loop infinitely in alternating directions because you said you wanted to make a ball "moving back and forth". - You'll also need a DOM node ref so you can pass that to the
gsap.context()
method. Create the ref in your component and pass it to the wrapping element of the component. Here, I called mine app (const app = useRef(null)
) and then passed it to the top level div in the App component withref={app}
. Make sure you're passing the ref to a DOM node and not a React component (or you'll have to forward the ref down to a node child within it). Why are we using refs? Because refs are stable between rerenders of your components like state, but unlike state, modifying refs don't cause rerenders. The useRef hook returns an object with a single property calledcurrent
. Whatever you put in the ref is accessed via the current property. - Use a
useLayoutEffect()
hook instead ofuseEffect
. React guarantees that the code insideuseLayoutEffect
and any state updates scheduled inside it will be processed before the browser repaints the screen. TheuseEffect
hook doesn't prevent the browser from repainting. Most of the time we want this to be the case so our app doesn't slow-down. However, in this case the useLayoutEffect hook ensures that React has performed all DOM mutations, and the elements are accessible to gsap to animate them. - Inside the useLayoutEffect, is where you'll use the gsap context method. The gsap context method takes two arguments: a callback function and a reference to the component. The callback is where you can access your timeline (don't forget to access via the
current
property of the ref object) and run your animations. - There are two ways to target the elements that you're going to animate on your timeline: either use a ref to store the DOM node or via a selector. I used a selector with the
".box"
class for the box element. This is easy and it's nice because it will only select matching elements which are children of the current component. I used a ref for the circle component. I included this as an example, so you could see how to use forwardRefs to pass the ref from the App component through Circle component to the child div DOM node. Even though this is a more "React-like" approach, it's harder and less flexible if you have a lot of elements to animate. - Just like
useEffect
,useLayoutEffect
returns a clean up function. Conveniently, the gsap context object has a clean up method calledrevert
.
import { useLayoutEffect, useRef } from "react";
import gsap from "gsap";
const timeline = gsap.timeline({ repeat: -1, yoyo: true });
function App() {
const tl = useRef(timeline);
const app = useRef(null);
const circle = useRef(null);
useLayoutEffect(() => {
const ctx = gsap.context(() => {
tl.current
// use scoped selectors
// i.e., selects matching children only
.to(".box", {
rotation: 360,
borderRadius: 0,
x: 100,
y: 100,
scale: 1.5,
duration: 1
})
// or refs
.to(circle.current, {
rotation: 360,
borderRadius: 50,
x: -100,
y: -100,
scale: 1.5,
duration: 1
});
}, app.current);
return () => ctx.revert();
}, []);
return (
<div ref={app} className="App">
<Box />
<Circle ref={circle} />
</div>
);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论