@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>