书接上回,在已经实现了3d模型的加载显示之后,我们就可以开始加入gsap来实现动画效果了。
这是gsap官方提供的文档,大家可以先看一看。
安装和引入gsap
先安装gsap
npm install @gsap/react
引入gsap, 先看一下官方提供的代码。
import { useRef } from 'react';
import gsap from 'gsap';
import { useGSAP } from '@gsap/react';
gsap.registerPlugin(useGSAP); // register the hook to avoid React version discrepancies
const container = useRef();
useGSAP(() => {
// gsap code here...
gsap.to('.box', { x: 360 }); // <-- automatically reverted
},{ scope: container }); // <-- scope is for selector text (optional)
这部分代码就包括了gsap的映入,已经实现了把box元素沿着x轴正向移动360的一个动画。
首先是gsap.registerPlugin(useGSAP); 这行代码是用来注册gsap的插件的,gsap提供了许多的插件来实现不同的效果,后面我们会用到一个将动画和屏幕滚动绑定在一起的一个插件,当然在这里useGSAP并不是gsap传统意义上的插件,他只是针对react而实现的一个hook,如果你用的不是react那么就不需要用到它。
useGSAP() 等效于 useEffect() 或 useLayoutEffect(),在useGSAP中创建的动画会在组件卸载或者注销时自动还原。
该钩子函数可在 Next.js 或其他服务端渲染环境中安全使用,但需确保用于客户端组件。它实现了同构布局效果技术(useIsomorphicLayoutEffect),优先使用 React 的 useLayoutEffect(),若未定义 window 对象则自动降级为 useEffect()。若你使用app router / react server components,需在文件顶部添加 "use client" 指令才能使 useGSAP() 正常工作。
下面是gsap提供的插件清单。
{ scope: container }是用来限制动画所绑定的元素范围,设置了这个之后,你用来绑定元素所使用选择器就只能是container元素的子元素的选择器,同时你在绑定元素的时候也不再需要使用ref。
gsap.to()是最常用的一个补间动画,用来把某个元素转变到某个特定状态,如移动到某个位置,旋转到某个角度等等。
要学的太多了,让我们先专注在我们的代码上🤘🏻。
实现动画
在我们之前的基础上,我们把代码修改一下。
'use client'
import { Canvas } from '@react-three/fiber'
import { useLoader } from '@react-three/fiber'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { OrbitControls, useGLTF } from "@react-three/drei";
import gsap from "gsap";
import { useGSAP } from "@gsap/react";
import { forwardRef, useRef, useEffect, useState } from 'react';
gsap.registerPlugin(useGSAP);
const Model = forwardRef((props, ref) => {
const gltf = useLoader(GLTFLoader, 'models/robot_gltf/scene.gltf');
// 使用 useEffect 确保模型加载完成后通知父组件
useEffect(() => {
if (gltf && props.onLoad) {
props.onLoad(true);
}
}, [gltf, props.onLoad]);
return <primitive object={gltf.scene} ref={ref} {...props} />
})
useGLTF.preload('models/robot_gltf/scene.gltf')
export default function Home() {
let container = useRef();
let myModel = useRef();
const [modelLoaded, setModelLoaded] = useState(false);
useGSAP(() => {
if (myModel.current && modelLoaded) {
console.log("模型已加载,应用动画");
gsap.to(myModel.current.position, {
duration: 2,
x: 2,
yoyo: true,
repeat: -1,
ease: "power1.inOut"
});
}
}, { scope: container, dependencies: [myModel, modelLoaded] });
return (
<div className='h-screen w-screen' ref={container}>
<Canvas>
<Model
ref={myModel}
onLoad={setModelLoaded}
/>
<OrbitControls />
<ambientLight intensity={0.5} />
<directionalLight
position={[5, 5, 5]}
intensity={1}
castShadow
shadow-mapSize-width={1024}
shadow-mapSize-height={1024}
/>
</Canvas>
</div>
);
}
在这段代码中我们在useGSAP中使用gsap.to中实现一个沿着x轴移动的动画,我们绑定的是模型的postion,也可以绑定rotation和其他属性。
- duration:表示动画时长
- x:表示沿x轴移动
- yoyo:表示动画会像悠悠球一样,在到达终点之后又回到起点
- repeat: -1表示重复播放动画
- ease:动画曲线,"power1.inOut"是gsap预设的动画曲线
在这段代码还有一个需要注意的点,就是我们使用了自定义的onLoad属性来监听模型是否加载完毕,在模型加载完毕之后再绑定动画,否则动画可能不会生效。
现在让我们来看一下效果。
最终效果
gif录的有点卡,大家见谅,实际效果大家运行了就知道了。