@threlte/extras
<Portal>
A component that renders its children as children of an object that can exist anywhere in your Threlte application.
You can use the prop id
to render into a <PortalTarget>
.
Although Portals are extremely helpful in certain situations, it can be hard to reason about them at times. It’s recommended to use them sparingly.
<Portal>
You might not need Some objects such as the THREE.DirectionalLightHelper
need to be added to the scene itself to be functional.
In this case, the portal target is easily accessible, and passing the scene
object to the <T>
component’s attach property can accomplish everything we need.
<script lang="ts">
import { Canvas } from '@threlte/core'
import Scene from './Scene.svelte'
</script>
<div>
<Canvas>
<Scene />
</Canvas>
</div>
<style>
div {
height: 100%;
background-color: rgb(47 125 198 / 0.2);
}
</style>
<script lang="ts">
import { T, useThrelte } from '@threlte/core'
import { Grid, OrbitControls, TransformControls } from '@threlte/extras'
const { scene } = useThrelte()
</script>
<T.PerspectiveCamera
position={[10, 10, 10]}
makeDefault
fov={30}
>
<OrbitControls enableZoom={false} />
</T.PerspectiveCamera>
<Grid />
<!-- Red main light -->
<T.DirectionalLight
color="#FE3D00"
intensity={1}
position={[1.5, 2, 0.5]}
>
{#snippet children({ ref })}
<T.DirectionalLightHelper
attach={scene}
args={[ref]}
>
{#snippet children({ ref: helperA })}
<TransformControls
object={ref}
onobjectChange={() => helperA.update()}
/>
{/snippet}
</T.DirectionalLightHelper>
{/snippet}
</T.DirectionalLight>
<!-- Blue rim light -->
<T.DirectionalLight
intensity={0.5}
color="#2F7DC6"
position={[-1, -2, 1]}
>
{#snippet children({ ref })}
<T.DirectionalLightHelper
attach={scene}
args={[ref]}
>
{#snippet children({ ref: helperB })}
<TransformControls
object={ref}
onobjectChange={() => helperB.update()}
/>
{/snippet}
</T.DirectionalLightHelper>
{/snippet}
</T.DirectionalLight>
<T.Mesh position.y={0.5}>
<T.SphereGeometry />
<T.MeshStandardMaterial color="white" />
</T.Mesh>
<PortalTarget>
Rendering to a For more complex cases where it’s hard to query the target, using <Portal>
may be an easier solution.
You can define where a <Portal>
should render its children by using the component <PortalTarget>
.
<script lang="ts">
import { Canvas } from '@threlte/core'
import Scene from './Scene.svelte'
</script>
<div>
<Canvas>
<Scene />
</Canvas>
</div>
<style>
div {
height: 100%;
background-color: rgb(47 125 198 / 0.2);
}
</style>
<script lang="ts">
import { T, useTask } from '@threlte/core'
import { Grid, OrbitControls, Portal, PortalTarget } from '@threlte/extras'
import { MathUtils } from 'three'
let posX = $state(Math.sin(Date.now() / 1000) * 4)
useTask(() => {
posX = Math.sin(Date.now() / 1000) * 4
})
</script>
<T.PerspectiveCamera
position={[10, 10, 10]}
makeDefault
fov={30}
>
<OrbitControls
maxPolarAngle={85 * MathUtils.DEG2RAD}
minPolarAngle={20 * MathUtils.DEG2RAD}
maxAzimuthAngle={45 * MathUtils.DEG2RAD}
minAzimuthAngle={-45 * MathUtils.DEG2RAD}
enableZoom={false}
/>
</T.PerspectiveCamera>
<Grid />
<T.DirectionalLight position={[5, 10, 3]} />
<T.Object3D
position.x={posX}
position.y={0.5}
>
<PortalTarget id="trail" />
</T.Object3D>
<Portal id="trail">
<T.Mesh>
<T.BoxGeometry />
<T.MeshStandardMaterial color="#FE3D00" />
</T.Mesh>
<T.Group position.y={1}>
<PortalTarget id="top" />
</T.Group>
</Portal>
<Portal id="top">
<T.Mesh>
<T.BoxGeometry />
<T.MeshStandardMaterial color="#2F7DC6" />
</T.Mesh>
</Portal>