Getting Started with GSAP + React
GSAP和React可以非常好的配好使用,已经有很多的网站在这样做了。GSAP是一个不挑框架的动画库,你完全可以在React中非常轻松的使用GSAP,还有像Vue、Regular等等任意的框架,只要你需要,都可以搭配上GSAP。这篇文章主要是来介绍在React中使用GSAP的技巧,让你可以更轻松的实现各种需求。
创建一个React应用
如果你喜欢在本地开发应用,可以用Create React App这个命令行工具,帮你快速的创建一个搭配GSAP和React的项目。
创建一个项目
项目创建后,要对项目进行初始化
把该引入的东西引入
实现交互动效
我们先来尝试一个小挑战,实现一个基于用户交互的动效。在React中实现这个效果很简单,我们可以通过某个事件回调函数来触发相应的动画执行。在下面这个案例中,onMouseEnter事件会触发元素放大,onMouseLeave事件会触发元素恢复。
但是如果我们想要一个动画在元素加载完之后,没有任何交互行为就会自动触发该如何实现呢?
元素加载后就执行动画 - useLayoutEffect()
useLayoutEffect()这个hook会React在所有DOM结构加载之后立即执行。用这个hook能保证在元素加载渲染完成后再执行动画。以下是一个代码结构案例:
使用Ref来指定元素
i
千万别忘了空的数组依赖。如果你忘了,那么React会在每次重新渲染的时候重新执行这个hook。
为了让元素变化,我们需要告诉GSAP我们要让哪个元素发生变化。React中获取DOM节点的方式就是通过Ref来获取。这个方式非常安全可靠。
然而,很多时候,我们需要给很多元素都设置动画。如果我们需要给每一个元素都设置一个Ref的话,那样简直是反人类。
所以我们可以使用一个特殊性的方式来解决这个看似头疼的问题,就是使用gsap.context()。
gsap.context()是你的好朋友
gsap.context()给React开发者提供两个非常棒的功能,一个是scoped selectors(范围内元素选择),另一个是更重要的 - animation cleanup(动画清理)。
i
GSAP Context和React Context是不一样的
Scope Selectors
我们可以给context传入一个Ref来指定一个区域范围,我们就可以在这个范围(子元素们)内,用gsap的方式,比如传入某个元素的类名来选中这些元素,让他们产生动画了。不需要去给每一个元素都设置一个Ref。
下面是代码示例:
下面这个demo中,React先是渲染了一个方块和一个圆形的DOM元素,然后GSAP把它们旋转了360度,当这些元素被卸载,这些动画会通过ctx.revert()方法清除。
深入一点
是用Refs 还是 scoped selectors ?
通过gsap.context()方式可以很方便的实现在React操作元素的实现动画效果。
需要注意的是,通过这个方式来实现的动画效果,是会穿透子层的。比如你在范围内部的子元素中还有子元素用的同一个类名,那么那个元素也会被驱动。
比如下面这个例子,整个app作为一个Scoped进行处理,然后通过scoped的方式驱动的是.box类名元素。右侧白色的边框的盒子,是和左侧的绿色盒子、紫色圆形平级的,它内部子元素-即绿色方块也用了.box的类名,因此也被驱动产生动画了。这就是我们说的穿透效果。
在旋转的circle元素是用Ref方式来驱动的,而白色边框中的紫色圆形,既没有单独设置ref也没有使用.box的类名,因此没有变化效果。
Cleaning up
清除
useLayoutEffect()给我们提供了清除函数,我们可以用跟他来清除动画效果。在React 18的strict 模式中,必要的动画清除对于避免一些意外情况是非常重要的。这样的方式也是符合React的最佳实践的。
gsap.context让清理这个事情变得非常简单高效。所有的GSAP动画和ScrollTriggers在创建时都被收集到了这个清除函数revert中,只要调用revert函数就能一次把它们都清理了。
你也可以利用清除函数去清除一些容易引起内存泄露的代码,比如事件监听函数。
i
gsap.matchMedia()其实内部是调用的gsap.context(),你可以直接在matchMedia的实例上调用revert方法来进行清除工作(没必要把他们结合起来)。
Reusing components
复用组件
在一个应用内,你可能会需要控制很多的元素进行变化,你可以把整个应用作为scoped范围,然后通过一些特定的类名或者属性参数来对元素进行分别的控制。
i
React建议使用类名来定义用户的样式,然后用data属性来配合实现一些JS功能的操作,比如动画效果。这篇文章中,我们主要是用类名,这样更方便理解。
Creating and controlling timelines
创建和控制时间线动画
到目前为止,我们只用Ref来引用DOM元素,但是他们并不是专门为执行DOM元素。Ref内储存的数据不会被重复渲染影响,所以他们能够用来储存一些你希望在整个组件的生命周期内都会使用到的数据。
为了避免每次重复渲染的时候会产生新的时间线动画实例,我们可以在useEffect中创建一个时间线动画实例,然后通过一个Ref储存起来。
为了避免每次重复渲染的时候会产生新的时间线动画实例,我们可以在useEffect中创建一个时间线动画实例,然后通过一个Ref储存起来。
Controlling when React creates our animation
注意空数组依赖
当通过useLayoutEffect方法出来创建gasp动画,由于组件会在状态更新时重新渲染,如果我们没有给useLayoutEffect传入空数组依赖,就是一个[],那么会造成不必要的重新渲染,如果传入了一个空数组作为依赖,那么,这个useLayoutEffect就只会在组件第一次渲染的时候执行一次,不会重复执行,关于这个依赖的问题你可以点击这里查看React相关的文档。
Reacting to changes in state
根据某个数据变化重新执行
如果你需要当组件中的某个数据发生改变时重新执行useLayoutEffect方法,那么你要在他的第二个参数,传入相应的数据。这样的做法在某些数据从组件外部传入时经常会用到。