Skip to content

Commit a2bc9d2

Browse files
committed
feat: react timeline components
1 parent 101912d commit a2bc9d2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1448
-392
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const camera = useThree((s) => s.camera)
3131
const redPill = useRef<Mesh>(null)
3232
const bluePill = useRef<Mesh>(null)
3333

34-
useTimeline(async function* () {
34+
useRunTimeline(async function* () {
3535
while (true) {
3636
//transition to look at the red pill
3737
yield* action({ update: lookAt(camera, redPill.current!, spring()) })

docs/getting-started/0-introduction.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const camera = useThree((s) => s.camera)
1919
const redPill = useRef<Mesh>(null)
2020
const bluePill = useRef<Mesh>(null)
2121

22-
useTimeline(async function* () {
22+
useRunTimeline(async function* () {
2323
while (true) {
2424
//transition to look at the red pill
2525
yield* action({ update: lookAt(camera, redPill.current!, spring()) })

docs/getting-started/1-what-is-a-timeline.mdx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,15 @@ yield * reusableFadeIn()
7272

7373
### Running timelines
7474

75-
- **React**: use `useTimeline(reusableTimeline, deps)`; it wires into `useFrame` and restarts on `deps` change.
76-
- **Vanilla**: use `start(reusableTimeline)` to get an update function you call each frame with `deltaSeconds`.
75+
- **React**: use `useRunTimelinereusableTimeline, deps)`; it wires into `useFrame` and restarts on `deps` change.
76+
- **Vanilla**: use `runTimeline(reusableTimeline)` to get an update function you call each frame with `deltaSeconds`.
7777

7878
```ts
7979
// React
80-
useTimeline(() => reusableFadeIn(), [someDep])
80+
useRunTimeline(() => reusableFadeIn(), [someDep])
8181

8282
// Vanilla
83-
const update = start(reusableFadeIn)
83+
const update = runTimeline(reusableFadeIn)
8484
// in your render loop
8585
update(state, deltaSeconds)
8686
```

docs/getting-started/2-first-timeline.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ This walks you through building the intro example from scratch.
2323
import { Text, Environment } from '@react-three/drei'
2424
import { useRef } from 'react'
2525
import { Mesh } from 'three'
26-
import { useTimeline, action, lookAt, spring, springPresets } from '@react-three/timeline'
26+
import { useRunTimeline, action, lookAt, spring, springPresets } from '@react-three/timeline'
2727
2828
function Scene() {
2929
const camera = useThree((s) => s.camera)
3030
const red = useRef<Mesh>(null)
3131
const blue = useRef<Mesh>(null)
3232
33-
useTimeline(async function* () {
33+
useRunTimeline(async function* () {
3434
while (true) {
3535
yield* action({ update: lookAt(camera, red.current!, spring(springPresets.stiff)) })
3636
yield* action({ update: lookAt(camera, blue.current!, spring(springPresets.stiff)) })
@@ -142,7 +142,7 @@ function Scene() {
142142
const red = useRef<Mesh>(null)
143143
const blue = useRef<Mesh>(null)
144144

145-
useTimeline(async function* () {
145+
useRunTimeline(async function* () {
146146
while (true) {
147147
yield* action({ update: lookAt(camera, red.current!, spring(springPresets.stiff)) })
148148
yield* action({ update: lookAt(camera, blue.current!, spring(springPresets.stiff)) })

docs/tutorials/5-parallel.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ The following demo shows how the left cube constantly moves since it has to cove
2525
'/App.tsx': `import { Canvas, useThree } from '@react-three/fiber'
2626
import { useRef } from 'react'
2727
import { Mesh, Vector3 } from 'three'
28-
import { useTimeline, action, parallel, transition, velocity } from '@react-three/timeline'
28+
import { useRunTimeline, action, parallel, transition, velocity } from '@react-three/timeline'
2929
3030
async function* moveTo(ref: React.RefObject<Mesh>, to: Vector3) {
3131
if (!ref.current) return
@@ -35,7 +35,7 @@ yield* action({ update: transition(ref.current.position, to, velocity(1, 5)) })
3535
function Scene() {
3636
const cubeA = useRef<Mesh>(null)
3737
const cubeB = useRef<Mesh>(null)
38-
useTimeline(async function* () {
38+
useRunTimeline(async function* () {
3939
while (true) {
4040
yield* parallel('all', moveTo(cubeA, new Vector3(-3.5, 0.5, 0)), moveTo(cubeB, new Vector3(1.5, -0.5, 0)))
4141
// Then nudge both back to origin, racing the first finisher
@@ -126,7 +126,7 @@ import { Vector3 } from 'three'
126126
function Scene() {
127127
const cubeA = useRef<Mesh>(null)
128128
const cubeB = useRef<Mesh>(null)
129-
useTimeline(async function* () {
129+
useRunTimeline(async function* () {
130130
while (true) {
131131
yield* parallel('all', moveTo(cubeA, new Vector3(-3.5, 0.5, 0)), moveTo(cubeB, new Vector3(1.5, -0.5, 0)))
132132
yield* parallel('all', moveTo(cubeA, new Vector3(0, 0, 0)), moveTo(cubeB, new Vector3(0, 0, 0)))

docs/tutorials/7-vanilla.mdx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ nav: 7
88

99
### Differences to React usage
1010

11-
- In React you use `useTimeline(...)` which ties into the render loop for you.
12-
- In vanilla you call `start(timeline)` and invoke the returned function every frame with `deltaSeconds` (the library internally clamps large deltas to ~1/30s for stability).
11+
- In React you use `useRunTimeline(...)` which ties into the render loop for you.
12+
- In vanilla you call `runTimeline(timeline)` and invoke the returned function every frame with `deltaSeconds` (the library internally clamps large deltas to ~1/30s for stability).
1313

1414
## Build a scene with `@pmndrs/timeline`
1515

@@ -25,7 +25,7 @@ Below is a complete example using two spheres and a camera that alternates betwe
2525
},
2626
}}
2727
files={{
28-
'/index.ts': `import { action, lookAt, spring, springPresets, start, timePassed } from '@pmndrs/timeline'
28+
'/index.ts': `import { action, lookAt, spring, springPresets, runTimeline, timePassed } from '@pmndrs/timeline'
2929
import { EffectComposer, RenderPass, EffectPass, BloomEffect, VignetteEffect } from 'postprocessing'
3030
import { PerspectiveCamera, Scene, WebGLRenderer, Color, Mesh, MeshPhysicalMaterial, SphereGeometry, ACESFilmicToneMapping, AmbientLight, SRGBColorSpace, PMREMGenerator, HalfFloatType, MeshStandardMaterial } from 'three'
3131
import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry.js'
@@ -110,7 +110,7 @@ async function* mainTimeline() {
110110
yield* action({ until: timePassed(0.3, 'seconds') })
111111
}
112112
}
113-
const update = start(mainTimeline())
113+
const update = runTimeline(mainTimeline())
114114
115115
// render loop
116116
let last = performance.now()
@@ -215,7 +215,7 @@ async function* mainTimeline() {
215215
yield* action({ until: timePassed(0.3, 'seconds') })
216216
}
217217
}
218-
const update = start(mainTimeline())
218+
const update = runTimeline(mainTimeline())
219219
```
220220

221221
5) Drive frames and handle resize

examples/showcase/lamborghini.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
springPresets,
1414
timePassed,
1515
transition,
16-
useTimeline,
16+
useRunTimeline,
1717
velocity,
1818
} from '@react-three/timeline'
1919
import { useEffect, useMemo } from 'react'
@@ -41,7 +41,7 @@ export function Lamborghini(props: any) {
4141
}, [scene])
4242
useEffect(() => scene.traverse((object) => (object.castShadow = true)), [scene])
4343
const camera = useThree((s) => s.camera)
44-
useTimeline(
44+
useRunTimeline(
4545
async function* () {
4646
const wheels = ['RR', 'RL', 'FR', 'FL'].map((name) => scene.getObjectByName(name))
4747
const frontWheels = ['FR', 'FL'].map((name) => scene.getObjectByName(name))

examples/simple/app.tsx

Lines changed: 78 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
import { Environment, Text } from '@react-three/drei'
22
import { Canvas, useThree } from '@react-three/fiber'
33
import { Bloom, EffectComposer, Vignette } from '@react-three/postprocessing'
4-
import { useTimeline, action, lookAt, spring, springPresets, timePassed } from '@react-three/timeline'
5-
import { useRef } from 'react'
4+
import {
5+
lookAt,
6+
RunTimeline,
7+
Loop,
8+
Sequential,
9+
SequentialEntry,
10+
Action,
11+
spring,
12+
springPresets,
13+
} from '@react-three/timeline'
14+
import { useControls } from 'leva'
15+
import { useState } from 'react'
616
import { Mesh } from 'three'
717

818
export function App() {
@@ -26,31 +36,78 @@ export function App() {
2636

2737
function Scene() {
2838
const camera = useThree((s) => s.camera)
29-
const redPill = useRef<Mesh>(null)
30-
const bluePill = useRef<Mesh>(null)
39+
const [redPill, setRedPill] = useState<Mesh | undefined>(undefined)
40+
const [greenPill, setGreenPill] = useState<Mesh | undefined>(undefined)
41+
const [bluePill, setBluePill] = useState<Mesh | undefined>(undefined)
3142

32-
useTimeline(async function* () {
33-
while (true) {
34-
yield* action({ update: lookAt(camera, redPill.current!, spring(springPresets.stiff)) })
35-
yield* action({ until: timePassed(0.3, 'seconds') })
36-
yield* action({ update: lookAt(camera, bluePill.current!, spring(springPresets.stiff)) })
37-
yield* action({ until: timePassed(0.3, 'seconds') })
38-
}
39-
}, [])
43+
const { blue, green, red } = useControls({ red: true, green: true, blue: true })
4044

4145
return (
4246
<>
43-
<Text position-y={1} scale={0.3}>
47+
<RunTimeline>
48+
<Loop>
49+
<Sequential>
50+
{redPill && red && (
51+
<SequentialEntry index={0}>
52+
<Action update={lookAt(camera, redPill, spring(springPresets.stiff))} />
53+
</SequentialEntry>
54+
)}
55+
{greenPill && green && (
56+
<SequentialEntry index={1}>
57+
<Action update={lookAt(camera, greenPill, spring(springPresets.stiff))} />
58+
</SequentialEntry>
59+
)}
60+
{bluePill && blue && (
61+
<SequentialEntry index={2}>
62+
<Action update={lookAt(camera, bluePill, spring(springPresets.stiff))} />
63+
</SequentialEntry>
64+
)}
65+
</Sequential>
66+
</Loop>
67+
</RunTimeline>
68+
69+
<Text position-y={0.6} scale={0.3}>
4470
Remember: all I'm offering is the truth. Nothing more.
4571
</Text>
46-
<mesh position-y={-1} position-x={-2} rotation-y={(-30 / 180) * Math.PI} scale={0.2} scale-z={0.4} ref={redPill}>
47-
<sphereGeometry />
48-
<meshPhysicalMaterial emissive="red" emissiveIntensity={0.5} color="red" />
49-
</mesh>
50-
<mesh position-y={-1} position-x={2} rotation-y={(20 / 180) * Math.PI} scale={0.2} scale-z={0.4} ref={bluePill}>
51-
<sphereGeometry />
52-
<meshPhysicalMaterial emissive="blue" emissiveIntensity={5} color="blue" />
53-
</mesh>
72+
{red && (
73+
<mesh
74+
position-y={-1}
75+
position-x={-2}
76+
rotation-y={(-30 / 180) * Math.PI}
77+
scale={0.2}
78+
scale-z={0.4}
79+
ref={setRedPill}
80+
>
81+
<sphereGeometry />
82+
<meshPhysicalMaterial emissive="red" emissiveIntensity={0.5} color="red" />
83+
</mesh>
84+
)}
85+
{blue && (
86+
<mesh
87+
position-y={2}
88+
position-x={0}
89+
rotation-y={(20 / 180) * Math.PI}
90+
scale={0.2}
91+
scale-z={0.4}
92+
ref={setBluePill}
93+
>
94+
<sphereGeometry />
95+
<meshPhysicalMaterial emissive="blue" emissiveIntensity={5} color="blue" />
96+
</mesh>
97+
)}
98+
{green && (
99+
<mesh
100+
position-y={-1}
101+
position-x={2}
102+
rotation-y={(20 / 180) * Math.PI}
103+
scale={0.2}
104+
scale-z={0.4}
105+
ref={setGreenPill}
106+
>
107+
<sphereGeometry />
108+
<meshPhysicalMaterial emissive="green" emissiveIntensity={5} color="green" />
109+
</mesh>
110+
)}
54111
</>
55112
)
56113
}

examples/simple/package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
{
22
"type": "module",
33
"dependencies": {
4-
"@react-three/fiber": "^9.2.0",
54
"@react-three/drei": "10.7.3",
5+
"@react-three/fiber": "^9.2.0",
6+
"@react-three/postprocessing": "3.0.4",
67
"@react-three/timeline": "workspace:^",
78
"@vitejs/plugin-react": "^4.6.0",
9+
"leva": "^0.10.0",
810
"react": "^19.1.0",
911
"react-dom": "^19.1.0",
10-
"three": "^0.178.0",
11-
"@react-three/postprocessing": "3.0.4"
12+
"three": "^0.178.0"
1213
},
1314
"devDependencies": {
1415
"@types/react": "^19.1.8",

examples/vanilla/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { action, lookAt, spring, springPresets, start, timePassed } from '@pmndrs/timeline'
1+
import { action, lookAt, spring, springPresets, runTimeline, timePassed } from '@pmndrs/timeline'
22
import { EffectComposer, RenderPass, EffectPass, BloomEffect, VignetteEffect } from 'postprocessing'
33
import {
44
PerspectiveCamera,
@@ -114,7 +114,7 @@ async function* mainTimeline() {
114114
}
115115
}
116116

117-
const update = start(mainTimeline())
117+
const update = runTimeline(mainTimeline())
118118

119119
// animate
120120
let lastTimeMs = performance.now()

0 commit comments

Comments
 (0)