Skip to content

Tween.js

Import

import TWEEN from '@tweenjs/tween.js'

Description

A tween (from in-between) is a concept that allows you to change the values of the properties of an object smoothly.

While Tween.js is not written with React Three Fiber in mind, it is still possible to use it.

Example 1 : Tween OrbitControls Target

In this example below, you can double-click the monkey head, or plane, and it will smoothly move the OrbitControls target to where you clicked the mesh.

<>

Using Tween.js in R3F

Install the dependency

npm install @tweenjs/tween.js --save-dev

Import in your script,

import TWEEN from '@tweenjs/tween.js'

Note that the TWEEN object is created as a singleton, which means that if you also import it in other files, then it will also point to the first instance that was registered in memory.

Next, we need to continually call TWEEN.Update(). So, creating a simple component, and calling it within a useFrame is sufficient.

function Tween() {
  useFrame(() => {
    TWEEN.update()
  })
}

And use the simple component it in your canvas.

<Canvas>
  // ...
  <Tween />
  // ...
</Canvas>

All tweens started throughout your application will be updated.

The TWEEN object will also manage its own delta time, so we don't need to pass that value in.

Note that you also only need one TWEEN.Update() being called within your whole application for it to manage all currently executing tweens throughout.

In the example above, I am tweening the target property of the OrbitControls. So in the Monkey, or Plane mesh, I have added a onDoubleClick event that will use the {point} object from the event to update the OrbitControls target.

I also need to pass the OrbitControls reference to the Monkey and Plane components when they are created, so that the onDoubleClick event can modify the target co-ordinates.

export default function App() {
  const ref = useRef()
  return (
    <>
        //...
        <OrbitControls ref={ref} />
        <Plane controls={ref} />  // Passing OrbitControls ref as a prop to the Plane
        //...
}
function Plane({ controls }) {
  return (
    <mesh
      // ...
      onDoubleClick={({ point }) => {
        new TWEEN.Tween(controls.current.target)
          .to(
            {
              x: point.x,
              y: point.y,
              z: point.z
            },
            500
          )
          .easing(TWEEN.Easing.Cubic.Out)
          .start()
      }}>
      // ...
  )
}

Example 2 : Tween Component Position

This time, there will be two tweens running simultaneously, and a third tween that starts after one of the other tweens has completed.

This will result in the Monkey appearing to bounce to the point where you've double-clicked the Plane.

<>

In order for the onDoubleClick event of the Plane to modify the Monkey position, I needed to pass in the Monkeyreference to it.

export default function App() {
  const ref = useRef()
  return (
    <>
        //...
        <Monkey ref={ref} />
        <Plane monkey={ref} /> <!-- Passing the Monkey ref as a prop to the Plane -->
        //...
}

And also since the Monkey is a custom function component within my app, and you can't create refs directly to function components, I needed to create a forwardRef to the Monkey for it to work.

const Monkey = forwardRef(function Monkey({ position }, ref) {
  // ...
  return (
    <group ref={ref}>
    // ...
})

And now I can configure multiple tweens to modify the Monkey when the Plane is double-clicked.

function Plane({ monkey }) {
  return (
    <mesh
      // ...
      onDoubleClick={({ point }) => {
        // slide
        new TWEEN.Tween(monkey.current.position)
          .to(
            {
              x: point.x,
              z: point.z
            },
            500
          )
          .start()

        // arc up through the air
        new TWEEN.Tween(monkey.current.position)
          .to(
            {
              y: point.y + 3
            },
            250
          )
          .easing(TWEEN.Easing.Cubic.Out)
          .start()
          .onComplete(() => {
            // arc down through the air and finish with a small bounce
            new TWEEN.Tween(monkey.current.position)
              .to(
                {
                  y: point.y + 1
                },
                250
              )
              .easing(TWEEN.Easing.Bounce.Out)
              .start()
          })
      }}>
      // ...
  )
}

The final little bounce when the Monkey lands is due to using TWEEN.Easing.Bounce.Out.

See Tween Easing Options for more possibilities.

Using tween.js sbcode
Tween OrbitControls sbedit.net
Tween Position sbedit.net
Tween.js github.com/tweenjs
User Guide tweenjs.github.io
Camera sbcode
Photogrammetry sbcode

GitHub Branch

git clone https://github.com/Sean-Bradley/React-Three-Fiber-Boilerplate.git
cd React-Three-Fiber-Boilerplate
git checkout tween
npm install
npm start

Comments