React Three Fiber support for react-spring animations
—
Animated versions of all Three.js objects and components for use with react-spring animations. The animated factory automatically creates animated versions of Three.js primitives that can accept spring values as props.
Creates animated versions of any React Three Fiber component that can accept spring values as props.
/**
* Creates an animated version of any component
* @param Component - React component to make animatable
* @returns Animated component that accepts spring values
*/
function animated<T extends ElementType>(Component: T): AnimatedComponent<T>;
// Short alias
const a = animated;Usage Examples:
import { animated, useSpring } from "@react-spring/three";
// Using built-in animated primitives
function AnimatedMesh() {
const springs = useSpring({
position: [0, 1, 0],
from: { position: [0, 0, 0] },
});
return (
<animated.mesh {...springs}>
<boxGeometry />
<meshStandardMaterial color="blue" />
</animated.mesh>
);
}
// Creating custom animated components
const AnimatedCustomComponent = animated(MyCustomThreeComponent);Pre-built animated versions of all Three.js components. These are automatically generated from Three.js class names by filtering all Three.js exported classes and converting their names to lowercase with the first letter lowercase.
type Primitives = keyof JSX.IntrinsicElements;
type AnimatedPrimitives = {
[P in Primitives]: AnimatedComponent<FC<JSX.IntrinsicElements[P]>>;
};
/**
* All Three.js classes are automatically converted to animated primitives.
* The primitive names are generated by:
* 1. Taking all Three.js exports that start with uppercase letter
* 2. Converting first letter to lowercase
*
* Examples of available animated primitives include:
* - Geometries: boxGeometry, sphereGeometry, cylinderGeometry, planeGeometry, etc.
* - Meshes: mesh, instancedMesh, skinnedMesh, etc.
* - Materials: meshBasicMaterial, meshStandardMaterial, meshPhysicalMaterial, etc.
* - Lights: ambientLight, directionalLight, pointLight, spotLight, etc.
* - Cameras: perspectiveCamera, orthographicCamera, etc.
* - Controls: orbitControls, transformControls, etc.
* - Plus the special 'primitive' component for arbitrary Three.js objects
*/
const primitives: Primitives[] = ['primitive'].concat(
Object.keys(THREE)
.filter(key => /^[A-Z]/.test(key))
.map(key => key[0].toLowerCase() + key.slice(1))
);Usage Examples:
import { animated, useSpring, useTrail } from "@react-spring/three";
// Animating mesh properties
function AnimatedCube() {
const { rotation, scale } = useSpring({
rotation: [0, Math.PI * 2, 0],
scale: [1.2, 1.2, 1.2],
from: { rotation: [0, 0, 0], scale: [1, 1, 1] },
loop: true,
});
return (
<animated.mesh rotation={rotation} scale={scale}>
<animated.boxGeometry args={[1, 1, 1]} />
<animated.meshStandardMaterial color="orange" />
</animated.mesh>
);
}
// Animating material properties
function AnimatedMaterial() {
const { color, opacity } = useSpring({
color: "#ff6b6b",
opacity: 0.8,
from: { color: "#4ecdc4", opacity: 0.2 },
});
return (
<mesh>
<boxGeometry />
<animated.meshStandardMaterial color={color} transparent opacity={opacity} />
</mesh>
);
}
// Trail animation for multiple objects
function AnimatedTrail() {
const trail = useTrail(5, {
position: [[0, 0, 0], [1, 0, 0], [2, 0, 0], [3, 0, 0], [4, 0, 0]],
from: { position: [0, -5, 0] },
});
return (
<>
{trail.map((springs, index) => (
<animated.mesh key={index} {...springs}>
<sphereGeometry args={[0.2]} />
<meshStandardMaterial color="red" />
</animated.mesh>
))}
</>
);
}Special animated primitive component for animating arbitrary Three.js objects.
/**
* Animated primitive component for arbitrary Three.js objects
*/
interface PrimitiveProps {
object: any; // Three.js object instance
[key: string]: any; // Additional props to animate
}Usage Examples:
import * as THREE from "three";
import { animated, useSpring } from "@react-spring/three";
function AnimatedCustomObject() {
const customGeometry = new THREE.TorusKnotGeometry(1, 0.3, 100, 16);
const springs = useSpring({
rotation: [0, Math.PI, 0],
from: { rotation: [0, 0, 0] },
});
return (
<animated.primitive object={customGeometry} {...springs}>
<meshStandardMaterial color="purple" />
</animated.primitive>
);
}import { CSSProperties, ForwardRefExoticComponent, FC, JSX, ElementType } from 'react';
import { AssignableKeys, ComponentPropsWithRef } from '@react-spring/types';
import { FluidValue } from '@react-spring/shared';
type AnimatedComponent<T extends ElementType> =
ForwardRefExoticComponent<AnimatedProps<ComponentPropsWithRef<T>>>;
type AnimatedProps<Props extends object> = {
[P in keyof Props]: P extends 'ref' | 'key'
? Props[P]
: AnimatedProp<Props[P]>;
};
// Complex type for animated prop values that handles style objects and arrays
type AnimatedProp<T> = [T, T] extends [infer T, infer DT]
? [DT] extends [never]
? never
: DT extends void
? undefined
: DT extends object
? [AssignableKeys<DT, CSSProperties>] extends [never]
? DT extends ReadonlyArray<any>
? AnimatedStyles<DT>
: DT
: AnimatedStyle<T>
: DT | AnimatedLeaf<T>
: never;
// Animated array of style objects
type AnimatedStyles<T extends ReadonlyArray<any>> = {
[P in keyof T]: [T[P]] extends [infer DT]
? DT extends object
? [AssignableKeys<DT, CSSProperties>] extends [never]
? DT extends ReadonlyArray<any>
? AnimatedStyles<DT>
: DT
: { [P in keyof DT]: AnimatedProp<DT[P]> }
: DT
: never;
};
// Animated object of style attributes
type AnimatedStyle<T> = [T, T] extends [infer T, infer DT]
? DT extends void
? undefined
: [DT] extends [never]
? never
: DT extends object
? { [P in keyof DT]: AnimatedStyle<DT[P]> }
: DT | AnimatedLeaf<T>
: never;
// Animated primitive (or an array of them)
type AnimatedLeaf<T> =
| Exclude<T, object | void>
| Extract<T, ReadonlyArray<number | string>> extends infer U
? [U] extends [never]
? never
: FluidValue<U | Exclude<T, object | void>>
: never;
// The type of the animated() function
type WithAnimated = {
<T extends ElementType>(wrappedComponent: T): AnimatedComponent<T>;
} & AnimatedPrimitives;Install with Tessl CLI
npx tessl i tessl/npm-react-spring--three