@threlte/extras
<Portal>
A component that renders its children as children of an object that can exist anywhere in your Threlte application.
You can either provide an object that will be the parent of the children or 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.
Examples
Render Helper Objects
Some objects such as the THREE.DirectionalLightHelper
need to be added to the scene instead of the light itself or another parent to be functional. We can use the <Portal>
component for that.
<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, Portal, TransformControls } from '@threlte/extras'
import type { DirectionalLightHelper } from 'three'
const { scene } = useThrelte()
let helperA: DirectionalLightHelper
let helperB: DirectionalLightHelper
</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 })}
<TransformControls
object={ref}
onobjectChange={() => {
if (!helperA) return
helperA.update()
}}
/>
<Portal object={scene}>
<T.DirectionalLightHelper
args={[ref]}
bind:ref={helperA}
/>
</Portal>
{/snippet}
</T.DirectionalLight>
<!-- Blue rim light -->
<T.DirectionalLight
intensity={0.5}
color="#2F7DC6"
position={[-1, -2, 1]}
>
{#snippet children({ ref })}
<TransformControls
object={ref}
onobjectChange={() => {
if (!helperB) return
helperB.update()
}}
/>
<Portal object={scene}>
<T.DirectionalLightHelper
args={[ref]}
bind:ref={helperB}
/>
</Portal>
{/snippet}
</T.DirectionalLight>
<T.Mesh position.y={0.5}>
<T.SphereGeometry />
<T.MeshStandardMaterial color="white" />
</T.Mesh>
<PortalTarget>
Render to a 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 = 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>