threlte logo
@threlte/extras

transitions

The plugin transitions enables Svelte-like transitions on Threlte components.

<script lang="ts">
  import { Canvas } from '@threlte/core'
  import Scene from './Scene.svelte'

  let showCube = true
</script>

<div class="relative h-full w-full">
  <div
    class="pointer-events-none absolute left-0 top-0 flex h-[60%] w-full items-center justify-center"
  >
    <button
      class="border-orange/5 text-orange pointer-events-auto rounded-sm border bg-orange-800/50 px-2 py-1 text-sm hover:bg-orange-800/70 hover:text-orange-400 focus:outline-none"
      on:click={() => {
        showCube = !showCube
      }}
    >
      {#if showCube}
        Hide Cube
      {:else}
        Show Cube
      {/if}
    </button>
  </div>
  <Canvas>
    <Scene {showCube} />
  </Canvas>
</div>
<script lang="ts">
  import { T } from '@threlte/core'
  import { createTransition } from '@threlte/extras'
  import { cubicOut } from 'svelte/easing'
  import type { Material, Mesh } from 'three'

  const fade = createTransition<Material>((ref) => {
    if (!ref.transparent) ref.transparent = true
    return {
      tick(t) {
        ref.opacity = t
      },
      easing: cubicOut,
      duration: 400
    }
  })

  const scale = createTransition<Mesh>((ref) => {
    return {
      tick(t) {
        // t is [0,1] and needs to be converted to [0.5,1]
        t = 0.5 + t * 0.5
        ref.scale.set(t, t, t)
      },
      easing: cubicOut,
      duration: 400
    }
  })

  const fly = createTransition<Mesh>((ref) => {
    return {
      tick(t) {
        // t is [0,1] and needs to be converted to [1,0]
        t = 1 - t
        ref.position.y = t
      },
      easing: cubicOut,
      duration: 400
    }
  })
</script>

<T.Mesh
  in={fly}
  out={scale}
>
  <T.BoxGeometry />
  <T.MeshStandardMaterial
    color="#A854F7"
    transition={fade}
  />
</T.Mesh>
<script lang="ts">
  import { T } from '@threlte/core'
  import { transitions } from '@threlte/extras'
  import { DEG2RAD } from 'three/src/math/MathUtils.js'
  import Cube from './Cube.svelte'

  export let showCube = true

  transitions()
</script>

<T.PerspectiveCamera
  position={[8, 5, 8]}
  on:create={({ ref }) => {
    ref.lookAt(0, 0, 0)
  }}
  makeDefault
/>

<T.DirectionalLight position={[3, 15, 10]} />
<T.AmbientLight />

{#if showCube}
  <Cube />
{/if}

<T.Mesh
  rotation.x={-90 * DEG2RAD}
  position.y={-0.5}
>
  <T.PlaneGeometry args={[4, 4]} />
  <T.MeshStandardMaterial color="#281543" />
</T.Mesh>

The plugin transitions hooks deep into the <T> Svelte component and changes to the runtime syntax of Svelte may break this plugin. If you encounter any issues, please open an issue on GitHub. It’s recommended to lock the version of Svelte to a specific version.

Usage

To use Threlte transitions, you need to inject the plugin first via invoking transitions(). All child <T> components will then accept transition properties.

createTransition

Threlte Transitions use Svelte transitions under the hood and therefore use a similar API. The function createTransition is used to conveniently create a transition.

import type { Material } from 'three'
import { createTransition } from '@threlte/extras'
import { cubicOut } from 'svelte/easing'

const fade = createTransition<Material>((ref) => {
  ref.transparent = true
  return {
    tick(t) {
      // t is [0,1]
      ref.opacity = t
    },
    easing: cubicOut,
    duration: 400,
    delay: 100
  }
})

The transition fade can now be applied to all <T> components that instantiate classes extending THREE.Material like THREE.MeshBasicMaterial or THREE.MeshStandardMaterial:

<T.MeshStandardMaterial transition={fade} />

Transition Directions

Run a transition only when the component mounts:

<T.MeshStandardMaterial in={fade} />

Run a transition only when the component unmounts:

<T.MeshStandardMaterial out={fade} />

Run a transition when the component mounts or unmounts:

<T.MeshStandardMaterial transition={fade} />

To react on different transition directions in the same transition, you can use the direction parameter:

import { createTransition } from '@threlte/extras'

// direction is 'in', 'out' or 'both'
const fly = createTransition((ref, { direction }) => {
  // …
})

Transition Parameters

To make reusing transitions throughout your application easier, make createTransition the return value of a function that accepts parameters:

import { createTransition } from '@threlte/extras'

const scale = (duration: number) => {
  return createTransition((ref) => {
    return {
      tick(t) {
        ref.scale.setScalar(t)
      },
      duration
    }
  })
}

The transition can now be used like this:

<T.Mesh transition={scale(400)} />

Transition Events

Similar to Svelte transitions, Threlte transitions also emit events:

{#if visible}
  <T.Mesh
    {geometry}
    {material}
    transition={fade}
    on:introstart={() => console.log('intro started')}
    on:outrostart={() => console.log('outro started')}
    on:introend={() => console.log('intro ended')}
    on:outroend={() => console.log('outro ended')}
  />
{/if}

Limitations

Transitions are always global

Threlte transitions cannot be declared as local and therefore transitions will run when any upstream component mounts or unmounts.

Threlte transitions always “intro”

As opposed to Svelte transitions, in and transition transitions will also run on the first render of a component.