这个指南将帮助您了解 refs,useFrame 以及如何使用 Fiber 实现基本动画

Basic Animations 基本动画实现

本教程假定您具备一些 React 知识,并且基于这个初始codesandbox,所以只需fork它并跟随教程即可!

我们将构建一个非常小的连续动画循环,这将是更高级动画的基础。

useFrame

useFrame 是 Fiber的hook,它允许您在 Fiber 的渲染循环的每一帧上执行代码。这可以有很多用途,但在这里我们将专注于使用它构建动画。

非常重要,一定要记住,Fiber 的 hook 只能在<Canvas />组件中调用。

import { useFrame } from '@react-three/fiber'

function MyAnimatedBox() {
  useFrame(() => {
    console.log("Hey, I'm executing every frame!")
  })
  return (
    <mesh>
      <boxGeometry />
      <meshBasicMaterial color="royalblue" />
    </mesh>
  )
}

这个循环是我们动画的基本构建方式,我们传递给 useFrame 的回调函数将在每一帧执行,并且会传递一个包含我们的 Fiber 场景状态的对象:

例如,我们可以从 clock 参数中提取时间信息,以了解应用程序中经过了多少时间,并使用该时间来使某个值持续发生变化:

useFrame(({ clock }) => {
  const a = clock.getElapsedTime()
  console.log(a) // the value will be 0 at scene initialization and grow each frame
})

clock 是 threejs Clock 类型的一个对象,可以获取到已经流逝的总时间,这对于实现动画是非常关键的。

Animating with Refs

通过 setState 更新组件状态并通过 props 更改 mesh 看起来很有诱惑力,但在处理连续更新(通常称为瞬态更新)时,通过setState不是理想的方式。相反,我们希望每帧直接改变我们的 mesh。首先,我们需要通过 useRef React hook 获取对它的引用:

import React from 'react'

function MyAnimatedBox() {
  const myMesh = React.useRef()
  return (
    <mesh ref={myMesh}>
      <boxGeometry />
      <meshBasicMaterial color="royalblue" />
    </mesh>
  )
}

通过 myMesh 这个 ref,我们能获取到 threejs 物体的直接引用,这样我们就可以通过 useFrame 直接进行设置,不需要担心 React 了。

useFrame(({ clock }) => {
  myMesh.current.rotation.x = clock.getElapsedTime()
})

让我们更深入了解下:

我们从传递给useFrame的参数中解构出clock,我们知道这是我们的Fiber场景的状态。

我们访问myMesh.current对象的rotation.x属性,这是我们mesh对象的引用。

我们将我们的时间数据分配给绕x轴旋转的角度,这意味着我们的对象现在将围绕x轴一致持续旋转(旋转角度不断增加,但是其实就是一圈一圈不停的转)。

练习:

尝试使用Math.sin(clock.getElapsedTime()) ,来看看动画的变化有啥不同。