@mehshekari/gsap-motion 3.1.1 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,161 +0,0 @@
1
- # Three.js and React Three Fiber
2
-
3
- GSAP is the **orchestration layer**, not the renderer. Three owns the scene,
4
- the camera, and the draw loop. GSAP owns *when* values change and *how* they
5
- ease between states.
6
-
7
- Neither library comes with GSAP, and most projects have neither. Check
8
- `package.json` before writing code against them, and say so if they are not
9
- there.
10
-
11
- ## The division
12
-
13
- | GSAP | Three / R3F |
14
- |---|---|
15
- | timing, easing, sequencing | geometry, materials, lights |
16
- | camera moves between positions | camera projection, the render loop |
17
- | object transforms over time | the transforms themselves |
18
- | material property transitions | shaders |
19
- | tying it all to scroll | rendering the result |
20
-
21
- Do not rebuild a physics engine or a particle system in GSAP. Do not hand-write
22
- easing in `useFrame` when a tween would say it in one line.
23
-
24
- ## The core pattern
25
-
26
- Three objects are plain JS objects, so GSAP tweens them directly. The only thing
27
- to remember is that nested properties are their own targets:
28
-
29
- ```ts
30
- gsap.to(mesh.position, { x: 2, y: 1, duration: 1.2, ease: "power3.inOut" });
31
- gsap.to(mesh.rotation, { y: Math.PI * 2, duration: 3, ease: "none", repeat: -1 });
32
- gsap.to(mesh.material, { opacity: 0, duration: 0.6 });
33
- gsap.to(mesh.scale, { x: 1.4, y: 1.4, z: 1.4, duration: 0.5 });
34
- ```
35
-
36
- `gsap.to(mesh, { position: { x: 2 } })` does not work. Target `mesh.position`.
37
-
38
- For a material property to animate opacity, `transparent` must already be true —
39
- setting it mid-tween causes a shader recompile.
40
-
41
- ## Uniforms
42
-
43
- Shader uniforms are `{ value }` objects, which tween cleanly:
44
-
45
- ```ts
46
- gsap.to(material.uniforms.uProgress, { value: 1, duration: 1.5, ease: "power2.inOut" });
47
- ```
48
-
49
- This is the usual bridge between a GSAP timeline and a custom shader effect —
50
- one uniform, driven by the timeline, interpreted by the shader.
51
-
52
- ## R3F
53
-
54
- The renderer runs its own loop, so nothing needs invalidating on demand unless
55
- the canvas is set to `frameloop="demand"` — in which case call `invalidate()`
56
- from `onUpdate`.
57
-
58
- ```tsx
59
- "use client";
60
-
61
- import { useGSAP } from "@gsap/react";
62
- import { useFrame, useThree } from "@react-three/fiber";
63
- import gsap from "gsap";
64
- import { useRef } from "react";
65
- import type { Mesh } from "three";
66
-
67
- export function Box() {
68
- const mesh = useRef<Mesh>(null);
69
-
70
- useGSAP(() => {
71
- if (!mesh.current) return;
72
- gsap.to(mesh.current.rotation, {
73
- y: Math.PI * 2,
74
- duration: 6,
75
- ease: "none",
76
- repeat: -1,
77
- });
78
- });
79
-
80
- return (
81
- <mesh ref={mesh}>
82
- <boxGeometry />
83
- <meshStandardMaterial />
84
- </mesh>
85
- );
86
- }
87
- ```
88
-
89
- **Never drive a 3D transform through React state.** `useState` per frame
90
- re-renders the React tree for a value Three reads directly. Refs and GSAP only.
91
-
92
- `useFrame` and GSAP are complementary, not competing: `useFrame` for continuous
93
- per-frame logic that depends on the clock, GSAP for transitions between states.
94
-
95
- ## Camera
96
-
97
- Camera moves are where GSAP earns its place — easing a camera by hand is how
98
- scroll-driven 3D ends up feeling mechanical.
99
-
100
- ```ts
101
- const { camera } = useThree();
102
-
103
- gsap.timeline({ defaults: { ease: "power2.inOut", duration: 2 } })
104
- .to(camera.position, { x: 0, y: 1.5, z: 4 })
105
- .to(camera.rotation, { x: -0.2 }, "<");
106
- ```
107
-
108
- If the camera uses `lookAt`, tween a proxy target and call `lookAt` in
109
- `onUpdate` — otherwise the rotation you tween is overwritten every frame:
110
-
111
- ```ts
112
- const look = { x: 0, y: 0, z: 0 };
113
- gsap.to(look, {
114
- x: 2,
115
- duration: 2,
116
- onUpdate: () => camera.lookAt(look.x, look.y, look.z),
117
- });
118
- ```
119
-
120
- ## Scroll-driven 3D
121
-
122
- Scrub a timeline, do not compute from `scrollY` in `useFrame`:
123
-
124
- ```ts
125
- gsap.timeline({
126
- scrollTrigger: { trigger: section, start: "top top", end: "+=2000",
127
- pin: true, scrub: 1 },
128
- })
129
- .to(camera.position, { z: 3, ease: "none" })
130
- .to(material.uniforms.uProgress, { value: 1, ease: "none" }, "<");
131
- ```
132
-
133
- `ease: "none"` on every child, as with any scrub. See
134
- [scrolltrigger.md](scrolltrigger.md).
135
-
136
- ## Cost and accessibility
137
-
138
- 3D is the most expensive thing on a page, and the constraints are harder than
139
- for DOM animation:
140
-
141
- - A WebGL canvas runs the GPU continuously. On a phone that is battery and heat.
142
- - Give the canvas a real fallback — a static image — for low-power devices and
143
- for `prefers-reduced-motion`. Do not merely stop the animation and leave a
144
- running canvas.
145
- - Dynamically import the 3D component so Three is not in the initial bundle.
146
- - `frameloop="demand"` when the scene is static between interactions.
147
- - Dispose geometries, materials and textures on unmount; GSAP's cleanup does not
148
- do that.
149
- - Nothing essential — text, navigation, product information — should exist only
150
- inside the canvas.
151
-
152
- ## Cleanup
153
-
154
- ```ts
155
- useGSAP(() => {
156
- const tl = gsap.timeline({ scrollTrigger: { ... } });
157
- return () => { tl.scrollTrigger?.kill(); };
158
- });
159
- ```
160
-
161
- Plus Three's own disposal, which is separate and yours to do.