threlte logo
@threlte/extras

<Float>

This component is a port of drei’s <Float> component and makes its contents float or hover.

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

<div>
  <Canvas>
    <Scene />
  </Canvas>
</div>

<style>
  div {
    height: 100%;
  }
</style>
<script lang="ts">
  import { T } from '@threlte/core'
  import { Float } from '@threlte/extras'
  import { onDestroy } from 'svelte'
  import { spring } from 'svelte/motion'
  import { Color, MeshPhysicalMaterial, type BufferGeometry } from 'three'

  export let geometry: BufferGeometry

  const red = new Color(0xfe3d00)
  const blue = new Color(0x0000ff)

  let material = new MeshPhysicalMaterial({
    color: red,
    reflectivity: 1,
    metalness: 0.9,
    roughness: 0.2
  })
  onDestroy(() => {
    material.dispose()
  })

  const scale = spring(1)

  const onPointerEnter = () => {
    material.color = blue
    scale.set(1.1)
  }

  const onPointerLeave = () => {
    material.color = red
    scale.set(1)
  }
</script>

<Float
  floatIntensity={5}
  scale={$scale}
  rotationIntensity={2}
  rotationSpeed={[1, 0.5, 0.2]}
>
  <T.Mesh
    {geometry}
    {material}
    on:pointerenter={onPointerEnter}
    on:pointerleave={onPointerLeave}
  />
</Float>
<script lang="ts">
  import { T } from '@threlte/core'
  import { Environment, Float, Grid, interactivity, useGltf } from '@threlte/extras'
  import type { Mesh } from 'three'
  import Blob from './Blob.svelte'

  type Nodes = 'ball-1' | 'ball-2' | 'ball-3' | 'ball-4' | 'ball-5'

  const gltf = useGltf<{
    nodes: Record<Nodes, Mesh>
    materials: {}
  }>('/models/blobs/blobs.glb', {
    useDraco: true
  })

  interactivity()
</script>

<Environment
  path="/hdr/"
  files="shanghai_riverside_1k.hdr"
/>

<Float
  rotationIntensity={0.15}
  rotationSpeed={2}
>
  <T.PerspectiveCamera
    makeDefault
    position.y={10}
    position.z={10}
    fov={90}
    on:create={({ ref }) => {
      ref.lookAt(0, 0, 0)
    }}
  />
</Float>

<T.DirectionalLight
  position.y={10}
  position.z={10}
/>

<T.AmbientLight intensity={0.3} />

<Grid
  position.y={-10}
  sectionThickness={1}
  infiniteGrid
  cellColor="#dddddd"
  sectionColor="#ffffff"
  sectionSize={10}
  cellSize={2}
/>

{#if $gltf}
  {#each Object.values($gltf.nodes) as node}
    {#if node.geometry}
      <Blob geometry={node.geometry} />
    {/if}
  {/each}
{/if}

Examples

Basic Example

FloatingMesh.svelte
<script lang="ts">
  import { T } from '@threlte/core'
  import { Float } from '@threlte/extras'
</script>

<Float
  floatIntensity={5}
  scale={$scale}
  rotationIntensity={2}
>
  <T.Mesh>
    <T.MeshStandardMaterial color={'orange'} />
    <T.BoxGeometry args={[5, 5, 5]} />
  </T.Mesh>
</Float>

Floating

floatingRange determines the allowed range of position trasformation, and by extension the direction in which the children will move. If you provide a single number tuple, like [-0.1,0.1] the movement will happen on y axis. Alternatively you can provide an array of three tuples to change the movement axes. For example [[0,0],[0,0],[-0.5,0.5]] will move children between -0.5 and 0.5 relative to the starting position on the Z axis.

Rotation

Rotation is set by rotationSpeed and rotationIntensity. Both of them need to be different from 0 to enable rotation. Providing a number in either of them applies it to all three axes. You get more granual control by passing an array [x: number, y: number, z: number]. rotationSpeed is responsible for the speed of the animation and rotationIntensity for the angle.

seed is responsible for the starting state of the animations and is random by default. Setting to a fixed value to get a predictable starting result.

Component Signature

<Float> extends <T.Group> and supports all its props, slot props, bindings and events.

Props

name
type
required
default

floatingRange
[number, number] | [x: [number, number], y: [number, number], z: [number, number]]
no
[-0.1, 0.1]

floatIntensity
number | [x: number, y: number, z: number]
no
1

rotationIntensity
number | [x: number, y: number, z: number]
no
0

rotationSpeed
number | [x: number, y: number, z: number]
no
0

seed
number
no
Math.random() * 10000

speed
number | [x: number, y: number, z: number]
no
1