@threlte/extras
<LinearGradientTexture>
A reactive linear gradient texture. The underlying texture uses an OffscreenCanvas and a CanvasTexture and is assigned the same colorspace as the renderer.
<script lang="ts">
import Scene from './Scene.svelte'
import type { ToneMapping, Wrapping } from 'three'
import { Canvas } from '@threlte/core'
import { Color, Folder, List, Pane, Slider } from 'svelte-tweakpane-ui'
import {
ClampToEdgeWrapping,
MirroredRepeatWrapping,
RepeatWrapping,
ACESFilmicToneMapping,
AgXToneMapping,
CineonToneMapping,
LinearToneMapping,
NeutralToneMapping,
NoToneMapping,
ReinhardToneMapping
} from 'three'
const toneMappingOptions: Record<PropertyKey, ToneMapping> = {
ACESFilmic: ACESFilmicToneMapping,
AgX: AgXToneMapping,
Cineon: CineonToneMapping,
Linear: LinearToneMapping,
NeutralToneMapping,
None: NoToneMapping,
Reinhard: ReinhardToneMapping
}
const wrappingOptions: Record<PropertyKey, Wrapping> = {
ClampToEdge: ClampToEdgeWrapping,
MirroredRepeat: MirroredRepeatWrapping,
Repeat: RepeatWrapping
}
const canvasSize = 1024
let sceneClearColor = $state('#000000')
let sceneToneMapping = $state(AgXToneMapping)
let gradientStartColor = $state('#ff00ff')
let gradientEndColor = $state('#ffff00')
let gradientStartX = $state(0)
let gradientStartY = $state(0)
let gradientEndX = $state(0)
let gradientEndY = $state(canvasSize)
let textureCenterX = $state(0)
let textureCenterY = $state(0)
let textureOffsetX = $state(0)
let textureOffsetY = $state(0)
let textureRepeatX = $state(1)
let textureRepeatY = $state(1)
let textureRotationDegrees = $state(0)
let textureWrapS: Wrapping = $state(ClampToEdgeWrapping)
let textureWrapT: Wrapping = $state(ClampToEdgeWrapping)
let textureRotation = $derived((Math.PI / 180) * textureRotationDegrees)
</script>
<Pane position="fixed">
<List
bind:value={sceneToneMapping}
options={toneMappingOptions}
label="tone mapping"
/>
<Color
bind:value={sceneClearColor}
label="clear color"
/>
<Folder title="gradient props">
<Color
bind:value={gradientStartColor}
label="start color"
/>
<Color
bind:value={gradientEndColor}
label="end color"
/>
<Slider
bind:value={gradientStartX}
label="start x"
min={0}
max={canvasSize}
step={1}
/>
<Slider
bind:value={gradientStartY}
label="start y"
min={0}
max={canvasSize}
step={1}
/>
<Slider
bind:value={gradientEndX}
label="end x"
min={0}
max={canvasSize}
step={1}
/>
<Slider
bind:value={gradientEndY}
label="end y"
min={0}
max={canvasSize}
step={1}
/>
</Folder>
<Folder title="texture props">
<List
bind:value={textureWrapS}
label="wrapS"
options={wrappingOptions}
/>
<List
bind:value={textureWrapT}
label="wrapT"
options={wrappingOptions}
/>
<Slider
label="centerX"
bind:value={textureCenterX}
min={-0.5}
max={1.5}
step={0.5}
/>
<Slider
label="centerY"
bind:value={textureCenterY}
min={-0.5}
max={1.5}
step={0.5}
/>
<Slider
label="offsetX"
bind:value={textureOffsetX}
min={-2}
max={2}
step={1}
/>
<Slider
label="offsetY"
bind:value={textureOffsetY}
min={-2}
max={2}
step={1}
/>
<Slider
label="repeatX"
bind:value={textureRepeatX}
min={0}
max={5}
step={1}
/>
<Slider
label="repeatY"
bind:value={textureRepeatY}
min={0}
max={5}
step={1}
/>
<Slider
label="rotation (degrees)"
bind:value={textureRotationDegrees}
min={-360}
max={360}
step={1}
/>
</Folder>
</Pane>
<div>
<Canvas>
<Scene
{gradientEndColor}
{gradientEndX}
{gradientEndY}
{gradientStartColor}
{gradientStartX}
{gradientStartY}
{sceneClearColor}
{sceneToneMapping}
{textureCenterX}
{textureCenterY}
{textureOffsetX}
{textureOffsetY}
{textureRepeatX}
{textureRepeatY}
{textureRotation}
{textureWrapS}
{textureWrapT}
{canvasSize}
/>
</Canvas>
</div>
<style>
div {
height: 100%;
}
</style>
<script lang="ts">
import type { ColorRepresentation, ToneMapping, Wrapping } from 'three'
import type { GradientStop } from '@threlte/extras'
import { DoubleSide } from 'three'
import { LinearGradientTexture, OrbitControls } from '@threlte/extras'
import { T, useThrelte } from '@threlte/core'
type SceneProps = {
canvasSize: number
gradientEndColor: ColorRepresentation
gradientEndX: number
gradientEndY: number
gradientStartColor: ColorRepresentation
gradientStartX: number
gradientStartY: number
sceneClearColor: ColorRepresentation
sceneToneMapping: ToneMapping
textureCenterX: number
textureCenterY: number
textureOffsetX: number
textureOffsetY: number
textureRepeatX: number
textureRepeatY: number
textureRotation: number
textureWrapS: Wrapping
textureWrapT: Wrapping
}
let {
canvasSize,
gradientEndColor,
gradientEndX,
gradientEndY,
gradientStartColor,
gradientStartX,
gradientStartY,
sceneClearColor,
sceneToneMapping,
textureCenterX,
textureCenterY,
textureOffsetX,
textureOffsetY,
textureRepeatX,
textureRepeatY,
textureRotation,
textureWrapS,
textureWrapT
}: SceneProps = $props()
let stops: GradientStop[] = $derived([
{ color: gradientStartColor, offset: 0 },
{ color: gradientEndColor, offset: 1 }
])
const { invalidate, renderer, toneMapping } = useThrelte()
$effect(() => {
toneMapping.set(sceneToneMapping)
invalidate()
})
$effect(() => {
renderer.setClearColor(sceneClearColor)
invalidate()
})
</script>
<T.PerspectiveCamera
makeDefault
position.z={5}
>
<OrbitControls />
</T.PerspectiveCamera>
<T.Mesh scale={2}>
<T.PlaneGeometry />
<T.MeshBasicMaterial side={DoubleSide}>
<LinearGradientTexture
width={canvasSize}
height={canvasSize}
startX={gradientStartX}
startY={gradientStartY}
endX={gradientEndX}
endY={gradientEndY}
center.x={textureCenterX}
center.y={textureCenterY}
offset.x={textureOffsetX}
offset.y={textureOffsetY}
repeat.x={textureRepeatX}
repeat.y={textureRepeatY}
rotation={textureRotation}
wrapS={textureWrapS}
wrapT={textureWrapT}
{stops}
/>
</T.MeshBasicMaterial>
</T.Mesh>
Attaching the Texture
The texture is automatically attached to the map
property of its parent. You can disable this behaviour by setting the attach
prop to false
. This may be useful if you want to create the texture but use it somewhere else.
<script>
let texture = null
</script>
<LinearGradientTexture
attach={false}
bind:ref={texture}
/>
<SomeComponent {texture} />
Gradient Stops
<LinearGradientTexture>
accepts a stops
prop which is an array of color stops that define the gradient. A stop is defined by two things; an offset
and a color
. Gradient stops are identical to how you would use them with a 2D context, notably the offset
should be a number between 0 and 1 inclusive. Stop colors can be any valid color representation in ThreeJS. Here are a couple examples of valid stops.
<LinearGradientTexture
stops={[
{ color: 'black', offset: 0 },
{ color: 'white', offset: 1 }
]}
/>
<LinearGradientTexture
stops={[
{ color: '#00ffff', offset: 0 },
{ color: '#ff00ff', offset: 0.5 },
{ color: '#ffff00', offset: 1 }
]}
/>
You can even mix and match color representations
<LinearGradientTexture
stops={[
{ color: 'red', offset: 0 },
{ color: 0xff_00_00, offset: 0.25 },
{ color: 'rgb(255, 0, 0)', offset: 0.5 },
{ color: '#ff0000', offset: 0.75 },
{ color: new Color(new Color(new Color())).set(1, 0, 0), offset: 1 }
]}
/>
All of the colors above are valid representations of the color red.
Gradient Start Point and End Point
You can control the gradient start point and end point with the startX
, startY
, endX
, and endY
props. For example, the props for a gradient that starts at the bottom left corner of the texture and ends at the top right corner would be:
<LinearGradientTexture
startX={0}
startY={height}
endX={width}
endY={0}
/>
Adjusting Scene Colors
If the colors in your scene do not match the color in your stops, you may need to adjust the tone mapping of the scene. ToneMapping
constants are imported from the three library.
<script>
import { useThrelte } from '@threlte/core'
import { LinearToneMapping } from 'three'
const { toneMapping } = useThrelte()
toneMapping.set(LinearToneMapping)
</script>
Component Signature
<LinearGradientTexture>
extends
<T
.
CanvasTexture>
and supports all its props, slot props, bindings and events.