Wawa Sensei logo

Animations

Starter pack

Animations are keys to bring your 3D scene to life. They can be triggered by user interactions, scroll or time based and applied to 3D objects, lights and cameras.

Mastering animations is a key skill to create immersive experiences. The more techniques you know, the more you can express your creativity.

Note: The 3D model animations are covered in the models chapter.

Lerp

Lerp is a mathematical function that interpolates between two values. It is useful to animate a value from one point to another.

const value = THREE.MathUtils.lerp(start, end, t);

The first parameter is the start value, the second parameter is the end value and the third parameter is the interpolation factor between 0 and 1.

The closer the interpolation factor is to 0, the slower the animation will be. The closer the interpolation factor is to 1, the faster the animation will reach the end or target value.

Let's experiment it on the AnimatedBox component from the starter pack.

The component has a boxPositions array that contains the positions of the box at different times. Let's add a useFrame hook to update the position of the box based on the time:

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

export const AnimatedBox = ({ boxPositions, ...props }) => {
  const box = useRef();

  useFrame(({ clock }) => {
    const seconds = parseInt(clock.getElapsedTime());
    const targetPosition = boxPositions[seconds % boxPositions.length];

    box.current.position.x = targetPosition.x;
    box.current.position.y = targetPosition.y;
    box.current.position.z = targetPosition.z;
  });
  // ...
};

Here our box is not animated. It teleports from one position to another. Let's use the lerp function to animate it:

import * as THREE from "three";
// ...

export const AnimatedBox = ({ boxPositions, ...props }) => {
  const box = useRef();

  useFrame(({ clock }) => {
    const seconds = parseInt(clock.getElapsedTime());
    const targetPosition = boxPositions[seconds % boxPositions.length];
    box.current.position.x = THREE.MathUtils.lerp(
      box.current.position.x,
      targetPosition.x,
      0.05
    );
    box.current.position.y = THREE.MathUtils.lerp(
      box.current.position.y,
      targetPosition.y,
      0.05
    );
    box.current.position.z = THREE.MathUtils.lerp(
      box.current.position.z,
      targetPosition.z,
      0.05
    );
  });
  // ...
};

The box is now animated. It moves smoothly from one position to another.

The Vector3 class has a lerp method that can be used instead of the THREE.MathUtils.lerp function:

// box.current.position.x = THREE.MathUtils.lerp(
//   box.current.position.x,
//   targetPosition.x,
//   0.05
// );
// box.current.position.y = THREE.MathUtils.lerp(
//   box.current.position.y,
//   targetPosition.y,
//   0.05
// );
// box.current.position.z = THREE.MathUtils.lerp(
//   box.current.position.z,
//   targetPosition.z,
//   0.05
// );
box.current.position.lerp(targetPosition, 0.05);

That's a lot of lines saved for the same result!

Don't hesitate to play with the time and to lerp on other properties like the rotation or the scale to try different effects.

Float

Float is a wrapper component from Drei library to simply give the impression that an object is floating.

It contains the following props:

  • speed: Animation speed, defaults to 1
  • rotationIntensity: XYZ rotation intensity, defaults to 1
  • floatIntensity: Up/down float intensity, works like a multiplier with floatingRange,defaults to 1
  • floatingRange: Range of y-axis values the object will float within, defaults to [-0.1,0.1]

Let's make our Duck float in Experience.jsx:

End of lesson preview

To get access to the entire lesson, you need to purchase the course.