Skip to content

This tutorial will assume some Vue knowledge, and will be based on this starter codesandbox, so just fork it and follow along!

We will build a really small, continuous animation loop, that will be the basic building block of more advanced animations later on.

useFrame

useFrame is a Fiber composable that lets you execute code on every frame of Fiber's render loop. This can have a lot of uses, but we will focus on building an animation with it.

It's important to remember that Fiber composables can only be called inside a <Canvas /> parent!

vue
<script setup>
import { useFrame } from '@bluera/vue-threejs'

useFrame(() => {
  console.log("Hey, I'm executing every frame!")
})
</script>

<template>
  <mesh>
    <boxGeometry />
    <meshBasicMaterial color="royalblue" />
  </mesh>
</template>

This loop is the basic building block of our animation, the callback we pass to useFrame will be executed every frame and it will be passed an object containing the state of our Fiber scene:

For example, we can extract time information from the clock parameter, to know how much time has elapsed in our application, and use that time to animate a value:

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

clock is a three.js Clock object, from which we are getting the total elapsed time, which will be key for our animations.

Animating with Refs

It would be tempting to just update the reactive state of our component and let it change the mesh via props, but going through reactive state isn't ideal when dealing with continuous updates, commonly known as transient updates. Instead, we want to directly mutate our mesh each frame. First, we'll have to get a reference to it, via Vue's ref():

vue
<script setup>
import { ref } from 'vue'

const myMesh = ref()
</script>

<template>
  <mesh ref="myMesh">
    <boxGeometry />
    <meshBasicMaterial color="royalblue" />
  </mesh>
</template>

myMesh will now hold a reference to the mesh, which we can freely mutate in useFrame without triggering Vue reactivity. The ref is backed by the renderer's internal proxy — property access, method calls, and instanceof all work transparently:

js
useFrame(({ clock }) => {
  if (myMesh.value) myMesh.value.rotation.x = clock.elapsedTime
})

Let's have a closer look:

  • We are destructuring clock from the argument passed to useFrame, which we know is the state of our Fiber scene.
  • We are accessing the rotation.x property of myMesh.value object, which is a reference to our mesh object
  • We are assigning our time-dependent value to the rotation on the x axis, meaning our object will now infinitely rotate between -1 and 1 radians around the x axis!

TIP

Template refs in vue-threejs receive a proxy-backed handle, not the raw THREE.Object3D. This works seamlessly for in-scene mutation like the example above. If you need the actual Three.js object — for instance to pass to a physics engine or compare identity — see the Object Handles tutorial.

Exercises

  • Try Math.sin(clock.elapsedTime) and see how your animation changes

Next steps

Now that you understand the basic technique for animating in Fiber, learn how events work!

If you want to go deeper into animations, keep exploring the Fiber render loop and combine it with Vue transitions or your animation library of choice.