threlte logo
@threlte/extras

<Align>

This component will calculate a boundary box and align its children accordingly.

<script lang="ts">
  import { Canvas } from '@threlte/core'
  import Scene from './Scene.svelte'
  import { Pane, Slider, Checkbox } from 'svelte-tweakpane-ui'

  let x = 0
  let y = 0
  let z = 0
  let precise = false
  let showSphere = true
  let autoAlign = true
</script>

<Pane
  title="Align"
  position="fixed"
>
  <Slider
    bind:value={x}
    label="X"
    min={-1}
    max={1}
  />
  <Slider
    bind:value={y}
    label="Y"
    min={-1}
    max={1}
  />
  <Slider
    bind:value={z}
    label="Z"
    min={-1}
    max={1}
  />
  <Checkbox
    label="Precise"
    bind:value={precise}
  />
  <Checkbox
    label="Show Sphere"
    bind:value={showSphere}
  />
  <Checkbox
    label="Auto Align"
    bind:value={autoAlign}
  />
</Pane>

<div>
  <Canvas>
    <Scene
      {x}
      {y}
      {z}
      {precise}
      {showSphere}
      {autoAlign}
    />
  </Canvas>
</div>

<style>
  div {
    height: 100%;
  }
</style>
<script lang="ts">
  import { T } from '@threlte/core'
  import { Align, OrbitControls, RoundedBoxGeometry, TransformControls } from '@threlte/extras'
  import { Box3, type Vector3 } from 'three'

  export let x: number = 0
  export let y: number = 0
  export let z: number = 0
  export let precise: boolean = false
  export let showSphere: boolean = false
  export let autoAlign: boolean = false

  let box = new Box3()
  let center: Vector3 | undefined
</script>

<T.PerspectiveCamera
  makeDefault
  position.z={10}
>
  <OrbitControls />
</T.PerspectiveCamera>

<!-- The property autoAlign is not reactive, therefore we need to key it here. -->
{#key autoAlign}
  <Align
    {x}
    {y}
    {z}
    {precise}
    auto={autoAlign}
    onalign={({ boundingBox, center: newCenter }) => {
      box.copy(boundingBox)
      center = newCenter
    }}
  >
    {#snippet children({ align })}
      <TransformControls onobjectChange={align}>
        <T.Mesh>
          <RoundedBoxGeometry args={[1, 2, 1]} />
          <T.MeshStandardMaterial color="white" />
        </T.Mesh>
      </TransformControls>

      <T.Mesh
        position.x={-4}
        position.y={1}
      >
        <RoundedBoxGeometry args={[1, 2, 3]} />
        <T.MeshStandardMaterial color="white" />
      </T.Mesh>

      {#if showSphere}
        <T.Mesh
          position.x={-2}
          position.y={3}
        >
          <T.SphereGeometry />
          <T.MeshStandardMaterial color="white" />
        </T.Mesh>
      {/if}
    {/snippet}
  </Align>
{/key}

{#if box && center}
  <T.Group
    position.x={center.x}
    position.y={center.y}
    position.z={center.z}
  >
    <T.Box3Helper
      args={[box, 'white']}
      oncreate={() => {
        console.log('CREATE!')
      }}
    />
  </T.Group>
{/if}

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

<T.AxesHelper />

The grouped objects will be aligned on the x, y, and z axes by default.

<script>
  import { T } from '@threlte/core'
  import { Align } from '@threlte/extras'
</script>

<Align>
  <T.Mesh position={[-1, 0, 0]}>
    <T.BoxGeometry />
    <T.MeshBasicMaterial />
  </T.Mesh>

  <T.Mesh position={[1, 0, -2]}>
    <T.BoxGeometry args={[1, 5, 2]} />
    <T.MeshBasicMaterial />
  </T.Mesh>
</Align>

x, y, z

You can also specify a number between -1 and 1 to align the objects on a respective axis. For example, providing x={1} will align the objects to the left (with respect to the default camera), x={0} will center the children, and x={1} will align them to the right whereas x={false} will ignore that axis completely.

<script>
  import { T } from '@threlte/core'
  import { Align } from '@threlte/extras'
</script>

<!-- Align left on the x-axis, ignore the y- and z-axes -->
<Align x={-1} y={false} z={false}>
  <T.Mesh position={[-1, 0, 0]}>
    <T.BoxGeometry />
    <T.MeshBasicMaterial />

  <T.Mesh position={[1, 0, -2]}>
    <T.BoxGeometry args={[1, 5, 2]} />
    <T.MeshBasicMaterial />
  </T.Mesh>
</Align>

auto

By default, the component <Align> will calculate the bounding box and align its children when the component mounts or any relevant props change. To account for child objects being mounted or unmounted, use the property auto.

<script>
  import { T } from '@threlte/core'
  import { Align } from '@threlte/extras'

  export let showOtherCube = true
</script>

<Align auto>
  <T.Mesh position={[-1, 0, 0]}>
    <T.BoxGeometry />
    <T.MeshBasicMaterial />

  {#if showOtherCube}
    <T.Mesh position={[1, 0, -2]}>
      <T.BoxGeometry args={[1, 5, 2]} />
      <T.MeshBasicMaterial />
    </T.Mesh>
  {/if}
</Align>

Events

The component <Align> provides an event align which fires when the child objects have been aligned. The event payload contains the following properties:

type AlignEventData = {
  /** The outmost container group of the <Align> component */
  container: Object3D
  /** The width of the bounding box */
  width: number
  /** The height of the bounding box */
  height: number
  /** The depth of the bounding box */
  depth: number
  boundingBox: Box3
  boundingSphere: Sphere
  center: Vector3
  verticalAlignment: number
  horizontalAlignment: number
  depthAlignment: number
}
<Align
  onalign={({ width }) => {
    console.log('The width of the bounding box is', width)
  }}
>
  <T.Mesh position={[-1, 0, 0]}>
    <T.BoxGeometry />
    <T.MeshBasicMaterial />
  </T.Mesh>
</Align>

Snippet Props

The component <Align> provides a snippet prop called align to schedule aligning all child objects. Be aware that this will not immediately align the objects, but rather schedule the alignment to happen exactly once per frame. It’s a manual alternative to auto.

<Align>
  {#snippet children({ align })}
    {#if showOtherCube}
      <T.Mesh oncreate={align}>
        <T.BoxGeometry />
        <T.MeshBasicMaterial />
      </T.Mesh>
    {/if}
  {/snippet}
</Align>

Component Signature

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

Props

name
type
required
default
description

auto
boolean
no
false
Injects a plugin in all child `<T>` components to automatically align whenever a component mounts or unmounts.

precise
boolean
no
false
See [setFromObject](https://threejs.org/docs/index.html?q=box3#api/en/math/Box3.setFromObject)

stage
Stage
no
useStage("<Align>", { before: renderStage })
Bring your own stage to control when aligning occurs. If not provided, aligning will occur before the main render stage.

x
number | false
no
0
Align child objects on the x-axis. If a number between -1 and 1 is provided, it will be used as the alignment on the x-axis. If `false` is provided, this axis will be ignored.

y
number | false
no
0
Align child objects on the y-axis. If a number between -1 and 1 is provided, it will be used as the alignment on the y-axis. If `false` is provided, this axis will be ignored.

z
number | false
no
0
Align child objects on the z-axis. If a number between -1 and 1 is provided, it will be used as the alignment on the z-axis. If `false` is provided, this axis will be ignored.

Events

name
payload
description

align
{ container: Object3D, width: number, height: number, depth: number, boundingBox: Box3, boundingSphere: Sphere, align: Vector3, verticalAlignment: number, horizontalAlignment: number, depthAlignment: number }
Fires when the child objects have been aligned.

Exports

name
type
description

align
() => void
Manually trigger an alignment.