发布于 2026-01-06 1 阅读
0

使用 React Three Fiber 和 GSAP DEV 的全球展示挑战赛(由 Mux 呈现):展示你的项目!

使用 React Three Fiber 和 GSAP 实现滚动动画

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

让我们为 3D 模型和用户界面添加动画效果,使其能够跟随页面滚动:

  • 维特
  • React
  • 顺风
  • Three.js
  • React 三纤维
  • GSAP

🔥 本教程是打造精美作品集的良好起点。

此外,还有视频版本可供观看最终渲染效果:

项目设置

让我们先用 Vite 创建一个 React 应用。

yarn create vite
Enter fullscreen mode Exit fullscreen mode

选择 React/Javascript 模板

使用 Vite Create 应用程序进行终端屏幕截图

现在添加 React Three Fiber 的依赖项。

yarn add three @react-three/drei @react-three/fiber
yarn dev
Enter fullscreen mode Exit fullscreen mode

请前往index.css并删除其中的所有内容(保留该文件,我们稍后会用到)。

App.css

#root {
  width: 100vw;
  height: 100vh;
  background-color: #d9afd9;
  background-image: linear-gradient(0deg, #d9afd9 0%, #97d9e1 100%);
}
Enter fullscreen mode Exit fullscreen mode

现在创建一个名为“.”的文件夹components,并在其中创建一个Experience.jsx文件。我们将在这里构建我们的3D体验。

让我们在里面创建一个立方体,并添加OrbitControlsReact Three Drei:

import { OrbitControls } from "@react-three/drei";

export const Experience = () => {
  return (
    <>
      <OrbitControls />
      <mesh>
        <boxBufferGeometry />
        <meshNormalMaterial />
      </mesh>
    </>
  );
};

Enter fullscreen mode Exit fullscreen mode

现在让我们打开App.jsx并替换内容,Canvas使其包含我们的 Three.js 组件和Experience我们刚刚构建的组件。

import { Canvas } from "@react-three/fiber";
import "./App.css";
import { Experience } from "./components/Experience";

function App() {
  return (
    <Canvas>
      <Experience />
    </Canvas>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

保存并运行项目

yarn dev
Enter fullscreen mode Exit fullscreen mode

你应该能看到一个立方体,并且可以用鼠标绕着它旋转(感谢…… OrbitControls)。

3D立方体的首次渲染图

加载3D模型

你可以从这里获取模型。

别忘了感谢Thaís为我们制作了这个漂亮的模型🙏

现在在你的终端中运行

npx gltfjsx publics/models/WawaOffice.glb
Enter fullscreen mode Exit fullscreen mode

gtlfjsx是一个客户端,可以根据你的 3D 模型自动创建 React 组件。它甚至还支持 TypeScript。

你应该已经生成了 WawaOffice.js 文件。

生成的 React 组件的源代码

复制所有内容,Office.jsx在文件夹中创建components并粘贴组件。

将组件名称从Model“to”更改为“to Òffice”,并将路径修复为“to”。./models/WawaOffice.glb

现在你的办公室应该像这样了。

/*
Auto-generated by: https://github.com/pmndrs/gltfjsx
*/

import React, { useRef } from 'react'
import { useGLTF } from '@react-three/drei'

export function Office(props) {
  const { nodes, materials } = useGLTF('./models/WawaOffice.glb')
  return (
    <group {...props} dispose={null}>
      <mesh geometry={nodes['01_office'].geometry} material={materials['01']} />
      <mesh geometry={nodes['02_library'].geometry} material={materials['02']} position={[0, 2.11, -2.23]} />
      <mesh geometry={nodes['03_attic'].geometry} material={materials['03']} position={[-1.97, 4.23, -2.2]} />
    </group>
  )
}

useGLTF.preload('./models/WawaOffice.glb')
Enter fullscreen mode Exit fullscreen mode

现在用组件Experience.jsx替换,并添加一个,以避免看到黑色的模型。mesh<Office /><ambientLight intensity={1}/>

顺便一提,这个模型包含了烘焙纹理(所以文件比较大)。也就是说,所有的光照和阴影都是在 Blender 中制作的,然后通过光线追踪烘焙到纹理文件中,才有了这么好的效果。

在滚动时为模型添加动画效果

让我们把Office组件包装到ScrollControlsReact Three Drei 中

<ScrollControls pages={3} damping={0.25}>
    <Office />
</ScrollControls>
Enter fullscreen mode Exit fullscreen mode

pages是您想要的页面数量。假设一页的高度等于视口的高度。
damping是平滑因子。我使用 取得了不错的效果。0.25

更多信息请参阅文档

你应该会看到一个滚动条出现,但你无法滚动,因为它们OrbitControls捕获了滚动事件。

只需按如下方式禁用即可

<OrbitControls enableZoom={false} />
Enter fullscreen mode Exit fullscreen mode

为了控制我们的办公室动画,我们需要安装gsap库。

yarn add gsap
Enter fullscreen mode Exit fullscreen mode

前往Office.jsx并存储ref到主组。

const ref = useRef();

return (
    <group
      {...props}
      dispose={null}
      ref={ref}
Enter fullscreen mode Exit fullscreen mode

让我们gsap在 a 中创建 ou 时间线useLayoutEffect,并将组的 y 位置从其当前位置更新到 ,-FLOOR_HEIGHT * (NB_FLOORS - 1)持续时间为 2 秒。

export const FLOOR_HEIGHT = 2.3;
export const NB_FLOORS = 3;

export function Office(props) {
...

useLayoutEffect(() => {
    tl.current = gsap.timeline();

    // VERTICAL ANIMATION
    tl.current.to(
      ref.current.position,
      {
        duration: 2,
        y: -FLOOR_HEIGHT * (NB_FLOORS - 1),
      },
      0
    );
Enter fullscreen mode Exit fullscreen mode

我们使用 2 秒的持续时间,因为我们有 3 个页面:

  • 第一页和初始位置为 0 秒
  • 第二个是1秒
  • 第三页是动画的结尾(2秒)。

我们沿 Y 轴反向滚动办公室,因为我们滚动的是办公室本身,而不是摄像头。从下往上滚动时,我们需要降低办公室的垂直高度。

现在让我们播放动画。我们可以通过钩子访问滚动条useScroll,它包含一个偏移属性,其值介于 0 和 1 之间01用于表示当前的滚动百分比。

const scroll = useScroll();

useFrame(() => {
    tl.current.seek(scroll.offset * tl.current.duration());
  });
Enter fullscreen mode Exit fullscreen mode

现在,我们的Office滚动条会随着页面滚动而垂直滚动。

让我们运用同样的原理来制作地板位置和旋转的动画。

这是我最终的版本,但您可以根据自己的喜好进行调整!

/*
Auto-generated by: https://github.com/pmndrs/gltfjsx
*/

import { useGLTF, useScroll } from "@react-three/drei";
import { useFrame } from "@react-three/fiber";
import gsap from "gsap";
import React, { useLayoutEffect, useRef } from "react";

export const FLOOR_HEIGHT = 2.3;
export const NB_FLOORS = 3;

export function Office(props) {
  const { nodes, materials } = useGLTF("./models/WawaOffice.glb");
  const ref = useRef();
  const tl = useRef();
  const libraryRef = useRef();
  const atticRef = useRef();

  const scroll = useScroll();

  useFrame(() => {
    tl.current.seek(scroll.offset * tl.current.duration());
  });

  useLayoutEffect(() => {
    tl.current = gsap.timeline();

    // VERTICAL ANIMATION
    tl.current.to(
      ref.current.position,
      {
        duration: 2,
        y: -FLOOR_HEIGHT * (NB_FLOORS - 1),
      },
      0
    );

    // Office Rotation
    tl.current.to(
      ref.current.rotation,
      { duration: 1, x: 0, y: Math.PI / 6, z: 0 },
      0
    );
    tl.current.to(
      ref.current.rotation,
      { duration: 1, x: 0, y: -Math.PI / 6, z: 0 },
      1
    );

    // Office movement
    tl.current.to(
      ref.current.position,
      {
        duration: 1,
        x: -1,
        z: 2,
      },
      0
    );
    tl.current.to(
      ref.current.position,
      {
        duration: 1,
        x: 1,
        z: 2,
      },
      1
    );

    // LIBRARY FLOOR
    tl.current.from(
      libraryRef.current.position,
      {
        duration: 0.5,
        x: -2,
      },
      0.5
    );
    tl.current.from(
      libraryRef.current.rotation,
      {
        duration: 0.5,
        y: -Math.PI / 2,
      },
      0
    );

    // ATTIC
    tl.current.from(
      atticRef.current.position,
      {
        duration: 1.5,
        y: 2,
      },
      0
    );

    tl.current.from(
      atticRef.current.rotation,
      {
        duration: 0.5,
        y: Math.PI / 2,
      },
      1
    );

    tl.current.from(
      atticRef.current.position,
      {
        duration: 0.5,

        z: -2,
      },
      1.5
    );
  }, []);

  return (
    <group
      {...props}
      dispose={null}
      ref={ref}
      position={[0.5, -1, -1]}
      rotation={[0, -Math.PI / 3, 0]}
    >
      <mesh geometry={nodes["01_office"].geometry} material={materials["01"]} />
      <group position={[0, 2.11, -2.23]}>
        <group ref={libraryRef}>
          <mesh
            geometry={nodes["02_library"].geometry}
            material={materials["02"]}
          />
        </group>
      </group>
      <group position={[-1.97, 4.23, -2.2]}>
        <group ref={atticRef}>
          <mesh
            geometry={nodes["03_attic"].geometry}
            material={materials["03"]}
          />
        </group>
      </group>
    </group>
  );
}

useGLTF.preload("./models/WawaOffice.glb");
Enter fullscreen mode Exit fullscreen mode

现在,您可以根据页面滚动效果获得漂亮的动画效果。

使用 Tailwind 准备 UI

我们来创建一个用户界面。你可以使用任何你喜欢的样式工具,但我选择了我的最爱——Tailwind

yarn add -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
Enter fullscreen mode Exit fullscreen mode

它将生成一个tailwind.config.cjs替换内容的语句

/** @type {import('tailwindcss').Config} */

const defaultTheme = require("tailwindcss/defaultTheme");

module.exports = {
  content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
  theme: {
    extend: {
      serif: ["Playfair Display", ...defaultTheme.fontFamily.sans],
      sans: ["Poppins", ...defaultTheme.fontFamily.sans],
    },
  },
  plugins: [],
};

Enter fullscreen mode Exit fullscreen mode

它告诉 Tailwind 监视.html文件.jsx,并将默认字体更改为我从Google Fonts中选择的字体。

现在index.css补充:

@import url("https://fonts.googleapis.com/css2?family=Playfair+Display:wght@600&family=Poppins&display=swap");

@tailwind base;
@tailwind components;
@tailwind utilities;
Enter fullscreen mode Exit fullscreen mode

第一行是导入 Google 字体。

好了,现在Tailwind已经安装好了,让我们来创建用户界面吧。

创建一个名为Overlay以下内​​容的组件

import { Scroll } from "@react-three/drei";

const Section = (props) => {
  return (
    <section className={`h-screen flex flex-col justify-center p-10 ${
        props.right ? "items-end" : "items-start"
      }`}
      <div className="w-1/2 flex items-center justify-center">
        <div className="max-w-sm w-full">
          <div className="bg-white  rounded-lg px-8 py-12">
            {props.children}
          </div>
        </div>
      </div>
    </section>
  );
};

export const Overlay = () => {
  return (
    <Scroll html>
      <div class="w-screen">
        <Section>
          <h1 className="font-semibold font-serif text-2xl">
            Hello, I'm Wawa Sensei
          </h1>
          <p className="text-gray-500">Welcome to my beautiful portfolio</p>
          <p className="mt-3">I know:</p>
          <ul className="leading-9">
            <li>🧑‍💻 How to code</li>
            <li>🧑‍🏫 How to learn</li>
            <li>📦 How to deliver</li>
          </ul>
          <p className="animate-bounce  mt-6">↓</p>
        </Section>
        <Section right>
          <h1 className="font-semibold font-serif text-2xl">
            Here are my skillsets 🔥
          </h1>
          <p className="text-gray-500">PS: I never test</p>
          <p className="mt-3">
            <b>Frontend 🚀</b>
          </p>
          <ul className="leading-9">
            <li>ReactJS</li>
            <li>React Native</li>
            <li>VueJS</li>
            <li>Tailwind</li>
          </ul>
          <p className="mt-3">
            <b>Backend 🔬</b>
          </p>
          <ul className="leading-9">
            <li>NodeJS</li>
            <li>tRPC</li>
            <li>NestJS</li>
            <li>PostgreSQL</li>
          </ul>
          <p className="animate-bounce  mt-6">↓</p>
        </Section>
        <Section>
          <h1 className="font-semibold font-serif text-2xl">
            🤙 Call me maybe?
          </h1>
          <p className="text-gray-500">
            I'm very expensive but you won't regret it
          </p>
          <p className="mt-6 p-3 bg-slate-200 rounded-lg">
            📞 <a href="tel:(+42) 4242-4242-424242">(+42) 4242-4242-424242</a>
          </p>
        </Section>
      </div>
    </Scroll>
  );
};
Enter fullscreen mode Exit fullscreen mode

请注意,我们的主 div 被包裹在一个Scroll带有 prop 的组件中html,以便能够将其添加html到我们的 Canvas 中,并稍后访问滚动条。

现在将Overlay组件添加到旁边Office

<ScrollControls pages={3} damping={0.25}>       
    <Overlay />
    <Office />
</ScrollControls>
Enter fullscreen mode Exit fullscreen mode

界面已经准备就绪,每个Section高度的100vh滚动效果也都不错。但我们再添加一些透明度动画。

滚动时为用户界面添加动画效果

我们将根据滚动情况改变各部分的透明度。

为此,我们将它们的透明度存储在一个状态中。

const [opacityFirstSection, setOpacityFirstSection] = useState(1);
  const [opacitySecondSection, setOpacitySecondSection] = useState(1);
  const [opacityLastSection, setOpacityLastSection] = useState(1);
Enter fullscreen mode Exit fullscreen mode

然后useFrame我们使用可用的滚动钩子方法为它们添加动画效果(更多信息请点击这里)。

useFrame(() => {
    setOpacityFirstSection(1 - scroll.range(0, 1 / 3));
    setOpacitySecondSection(scroll.curve(1 / 3, 1 / 3));
    setOpacityLastSection(scroll.range(2 / 3, 1 / 3));
  });
Enter fullscreen mode Exit fullscreen mode

我们将不透明度作为属性添加到各个部分。

<Section opacity={opacityFirstSection}>
...
<Section right opacity={opacitySecondSection}>
...
<Section opacity={opacityLastSection}>
...
Enter fullscreen mode Exit fullscreen mode

现在,Section我们在组件中使用此属性来调整不透明度。

<section
      className={`h-screen flex flex-col justify-center p-10 ${
        props.right ? "items-end" : "items-start"
      }`}
      style={{
        opacity: props.opacity,
      }}
    >
Enter fullscreen mode Exit fullscreen mode

使用 3D Office 完成教程的最终渲染

结论

恭喜!您现在已经拥有了一个很好的起点,可以使用 React Three Fiber 和 Tailwind 构建自己的作品集。

实时预览

代码可在此处获取:
https://github.com/wass08/r3f-scrolling-animation-tutorial

我强烈建议您阅读React Three Fiber 的文档并查看其示例,以了解您可以实现什么以及如何实现。

想要学习更多 React Three Fiber 教程,可以查看我在 YouTube 上的Three.js/React Three Fiber播放列表。

谢谢,如有任何疑问,欢迎在评论区留言🙏

文章来源:https://dev.to/wawasensei/scroll-animations-with-react- Three-Fiber-and-gsap-273j