three.js+next.js+gsap示例(2)

290 阅读3分钟

书接上回,在已经实现了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提供的插件清单。

Pasted image 20250612212136.png

{ 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录的有点卡,大家见谅,实际效果大家运行了就知道了。

PixPin_2025-06-18_21-10-44.gif