@juniorxsound/react-three-components 0.1.2 → 0.1.4
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.
- package/README.md +98 -41
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/useGLTFMaterialVariants.d.ts +62 -0
- package/dist/index.d.ts +2 -0
- package/dist/react-three-components.js +472 -412
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
[](https://github.com/juniorxsound/react-three-components/actions/workflows/ci.yml)
|
|
5
5
|
[](https://github.com/juniorxsound/react-three-components/blob/main/LICENSE)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
A personal set of reusable components for React Three Fiber 🪄
|
|
8
|
+
|
|
9
|
+
> This library is a work in progress - if you find any issues, please report them [here](https://github.com/juniorxsound/react-three-components/issues). Please proceed with caution if you use this library in production.
|
|
8
10
|
|
|
9
11
|
## Installation
|
|
10
12
|
|
|
@@ -28,12 +30,15 @@ A 3D carousel that arranges items in a circle and rotates around an axis.
|
|
|
28
30
|
|
|
29
31
|
```tsx
|
|
30
32
|
import { Canvas } from "@react-three/fiber";
|
|
31
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
CircularCarousel,
|
|
35
|
+
useCarouselContext,
|
|
36
|
+
} from "@juniorxsound/react-three-components";
|
|
32
37
|
|
|
33
38
|
function Item({ index }: { index: number }) {
|
|
34
39
|
const { activeIndex } = useCarouselContext();
|
|
35
40
|
const isActive = index === activeIndex;
|
|
36
|
-
|
|
41
|
+
|
|
37
42
|
return (
|
|
38
43
|
<mesh>
|
|
39
44
|
<boxGeometry args={[1, 1, 1]} />
|
|
@@ -58,27 +63,27 @@ function App() {
|
|
|
58
63
|
|
|
59
64
|
#### Props
|
|
60
65
|
|
|
61
|
-
| Prop
|
|
62
|
-
|
|
63
|
-
| `children`
|
|
64
|
-
| `radius`
|
|
65
|
-
| `axis`
|
|
66
|
-
| `index`
|
|
67
|
-
| `defaultIndex`
|
|
68
|
-
| `onIndexChange`
|
|
69
|
-
| `dragEnabled`
|
|
70
|
-
| `dragSensitivity` | `number`
|
|
71
|
-
| `dragAxis`
|
|
72
|
-
| `dragConfig`
|
|
66
|
+
| Prop | Type | Default | Description |
|
|
67
|
+
| ----------------- | ------------------------- | -------- | ----------------------------- |
|
|
68
|
+
| `children` | `ReactNode` | required | Carousel items |
|
|
69
|
+
| `radius` | `number` | `3` | Distance from center to items |
|
|
70
|
+
| `axis` | `"x" \| "y" \| "z"` | `"y"` | Rotation axis |
|
|
71
|
+
| `index` | `number` | - | Controlled active index |
|
|
72
|
+
| `defaultIndex` | `number` | `0` | Initial index (uncontrolled) |
|
|
73
|
+
| `onIndexChange` | `(index: number) => void` | - | Called when index changes |
|
|
74
|
+
| `dragEnabled` | `boolean` | `true` | Enable drag gestures |
|
|
75
|
+
| `dragSensitivity` | `number` | auto | Drag sensitivity |
|
|
76
|
+
| `dragAxis` | `"x" \| "y"` | `"x"` | Drag gesture axis |
|
|
77
|
+
| `dragConfig` | `DragConfig` | - | Additional drag options |
|
|
73
78
|
|
|
74
79
|
#### Ref Methods
|
|
75
80
|
|
|
76
81
|
```tsx
|
|
77
82
|
const ref = useRef<CircularCarouselRef>(null);
|
|
78
83
|
|
|
79
|
-
ref.current.next();
|
|
80
|
-
ref.current.prev();
|
|
81
|
-
ref.current.goTo(2);
|
|
84
|
+
ref.current.next(); // Go to next item
|
|
85
|
+
ref.current.prev(); // Go to previous item
|
|
86
|
+
ref.current.goTo(2); // Go to specific index
|
|
82
87
|
```
|
|
83
88
|
|
|
84
89
|
#### With Navigation Triggers
|
|
@@ -88,13 +93,19 @@ ref.current.goTo(2); // Go to specific index
|
|
|
88
93
|
<Item index={0} />
|
|
89
94
|
<Item index={1} />
|
|
90
95
|
<Item index={2} />
|
|
91
|
-
|
|
96
|
+
|
|
92
97
|
<CircularCarousel.PrevTrigger position={[-2, 0, 0]}>
|
|
93
|
-
<mesh
|
|
98
|
+
<mesh>
|
|
99
|
+
<boxGeometry />
|
|
100
|
+
<meshBasicMaterial color="blue" />
|
|
101
|
+
</mesh>
|
|
94
102
|
</CircularCarousel.PrevTrigger>
|
|
95
|
-
|
|
103
|
+
|
|
96
104
|
<CircularCarousel.NextTrigger position={[2, 0, 0]}>
|
|
97
|
-
<mesh
|
|
105
|
+
<mesh>
|
|
106
|
+
<boxGeometry />
|
|
107
|
+
<meshBasicMaterial color="red" />
|
|
108
|
+
</mesh>
|
|
98
109
|
</CircularCarousel.NextTrigger>
|
|
99
110
|
</CircularCarousel>
|
|
100
111
|
```
|
|
@@ -107,12 +118,15 @@ A carousel that slides items linearly (horizontally or vertically).
|
|
|
107
118
|
|
|
108
119
|
```tsx
|
|
109
120
|
import { Canvas } from "@react-three/fiber";
|
|
110
|
-
import {
|
|
121
|
+
import {
|
|
122
|
+
LinearCarousel,
|
|
123
|
+
useLinearCarouselContext,
|
|
124
|
+
} from "@juniorxsound/react-three-components";
|
|
111
125
|
|
|
112
126
|
function Item({ index }: { index: number }) {
|
|
113
127
|
const { activeIndex } = useLinearCarouselContext();
|
|
114
128
|
const isActive = index === activeIndex;
|
|
115
|
-
|
|
129
|
+
|
|
116
130
|
return (
|
|
117
131
|
<mesh scale={isActive ? 1.2 : 1}>
|
|
118
132
|
<planeGeometry args={[2, 1.5]} />
|
|
@@ -137,27 +151,27 @@ function App() {
|
|
|
137
151
|
|
|
138
152
|
#### Props
|
|
139
153
|
|
|
140
|
-
| Prop
|
|
141
|
-
|
|
142
|
-
| `children`
|
|
143
|
-
| `gap`
|
|
144
|
-
| `direction`
|
|
145
|
-
| `index`
|
|
146
|
-
| `defaultIndex`
|
|
147
|
-
| `onIndexChange`
|
|
148
|
-
| `dragEnabled`
|
|
149
|
-
| `dragSensitivity` | `number`
|
|
150
|
-
| `dragAxis`
|
|
151
|
-
| `dragConfig`
|
|
154
|
+
| Prop | Type | Default | Description |
|
|
155
|
+
| ----------------- | ---------------------------- | -------------- | ---------------------------------- |
|
|
156
|
+
| `children` | `ReactNode` | required | Carousel items |
|
|
157
|
+
| `gap` | `number` | `0.2` | Space between items |
|
|
158
|
+
| `direction` | `"horizontal" \| "vertical"` | `"horizontal"` | Slide direction |
|
|
159
|
+
| `index` | `number` | - | Controlled active index |
|
|
160
|
+
| `defaultIndex` | `number` | `0` | Initial index (uncontrolled) |
|
|
161
|
+
| `onIndexChange` | `(index: number) => void` | - | Called when index changes |
|
|
162
|
+
| `dragEnabled` | `boolean` | `true` | Enable drag gestures |
|
|
163
|
+
| `dragSensitivity` | `number` | `150` | Drag sensitivity |
|
|
164
|
+
| `dragAxis` | `"x" \| "y"` | auto | Drag axis (derived from direction) |
|
|
165
|
+
| `dragConfig` | `DragConfig` | - | Additional drag options |
|
|
152
166
|
|
|
153
167
|
#### Ref Methods
|
|
154
168
|
|
|
155
169
|
```tsx
|
|
156
170
|
const ref = useRef<LinearCarouselRef>(null);
|
|
157
171
|
|
|
158
|
-
ref.current.next();
|
|
159
|
-
ref.current.prev();
|
|
160
|
-
ref.current.goTo(2);
|
|
172
|
+
ref.current.next(); // Go to next item (bounded)
|
|
173
|
+
ref.current.prev(); // Go to previous item (bounded)
|
|
174
|
+
ref.current.goTo(2); // Go to specific index
|
|
161
175
|
```
|
|
162
176
|
|
|
163
177
|
> Note: LinearCarousel is bounded (doesn't wrap around), unlike CircularCarousel which loops infinitely.
|
|
@@ -169,11 +183,11 @@ ref.current.goTo(2); // Go to specific index
|
|
|
169
183
|
<Item index={0} />
|
|
170
184
|
<Item index={1} />
|
|
171
185
|
<Item index={2} />
|
|
172
|
-
|
|
186
|
+
|
|
173
187
|
<LinearCarousel.PrevTrigger position={[-3, 0, 0]}>
|
|
174
188
|
<PrevButton />
|
|
175
189
|
</LinearCarousel.PrevTrigger>
|
|
176
|
-
|
|
190
|
+
|
|
177
191
|
<LinearCarousel.NextTrigger position={[3, 0, 0]}>
|
|
178
192
|
<NextButton />
|
|
179
193
|
</LinearCarousel.NextTrigger>
|
|
@@ -182,6 +196,49 @@ ref.current.goTo(2); // Go to specific index
|
|
|
182
196
|
|
|
183
197
|
---
|
|
184
198
|
|
|
199
|
+
### useGLTFMaterialVariants
|
|
200
|
+
|
|
201
|
+
Parse and assign [KHR_materials_variants](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_variants) on an already-loaded glTF. Load the model with `useGLTF` (Drei), `useLoader(GLTFLoader, url)`, or your own loader; then pass the result. The hook applies the first variant initially (or pass `{ variant: "name" }` to avoid flashing). Returns `variants`, `activeVariant`, and `setVariant`. Suspends until the variant is applied—wrap in a `<Suspense>` boundary.
|
|
202
|
+
|
|
203
|
+
```tsx
|
|
204
|
+
import { Suspense } from "react";
|
|
205
|
+
import { Canvas } from "@react-three/fiber";
|
|
206
|
+
import { useGLTF } from "@react-three/drei";
|
|
207
|
+
import { useGLTFMaterialVariants } from "@juniorxsound/react-three-components";
|
|
208
|
+
|
|
209
|
+
function Shoe() {
|
|
210
|
+
const gltf = useGLTF("/MaterialsVariantsShoe.gltf");
|
|
211
|
+
const { variants, activeVariant, setVariant } = useGLTFMaterialVariants(
|
|
212
|
+
gltf,
|
|
213
|
+
{ variant: "midnight" }
|
|
214
|
+
);
|
|
215
|
+
return (
|
|
216
|
+
<group>
|
|
217
|
+
<primitive object={gltf.scene} />
|
|
218
|
+
{/* Use setVariant(name) to switch variants */}
|
|
219
|
+
</group>
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function App() {
|
|
224
|
+
return (
|
|
225
|
+
<Canvas>
|
|
226
|
+
<Suspense fallback={null}>
|
|
227
|
+
<Shoe />
|
|
228
|
+
</Suspense>
|
|
229
|
+
</Canvas>
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
| Return | Type | Description |
|
|
235
|
+
| ------------- | ---------- | ------------------------------------ |
|
|
236
|
+
| `variants` | `string[]` | Variant names from the extension. |
|
|
237
|
+
| `activeVariant` | `string \| null` | Currently active variant name. |
|
|
238
|
+
| `setVariant` | `(name: string) => void` | Switch to a variant by name. |
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
185
242
|
## Context Hooks
|
|
186
243
|
|
|
187
244
|
Access carousel state from any child component:
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export { useCarouselDrag, type DragConfig, type SpringConfig, type UseCarouselDragOptions, type UseCarouselDragReturn, } from "./useCarouselDrag";
|
|
2
2
|
export { CarouselContext, useCarouselContext, type CarouselContextValue, } from "./useCarouselContext";
|
|
3
|
+
export { useGLTFMaterialVariants, type GLTFWithVariants, type UseGLTFMaterialVariantsOptions, type UseGLTFMaterialVariantsResult, } from "./useGLTFMaterialVariants";
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { Group, Mesh } from "three";
|
|
2
|
+
/** KHR_materials_variants extension on the root glTF result. */
|
|
3
|
+
type VariantsExtension = {
|
|
4
|
+
variants: Array<{
|
|
5
|
+
name: string;
|
|
6
|
+
}>;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* glTF result from useGLTF (Drei), useLoader(GLTFLoader, url), or GLTFLoader.loadAsync.
|
|
10
|
+
* Must include parser and userData.gltfExtensions for KHR_materials_variants.
|
|
11
|
+
*/
|
|
12
|
+
export type GLTFWithVariants = {
|
|
13
|
+
scene: Group;
|
|
14
|
+
parser: {
|
|
15
|
+
getDependency: (type: string, index: number) => Promise<unknown>;
|
|
16
|
+
assignFinalMaterial: (mesh: Mesh) => void;
|
|
17
|
+
};
|
|
18
|
+
userData: {
|
|
19
|
+
gltfExtensions?: Record<string, VariantsExtension>;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
/** Options for useGLTFMaterialVariants. */
|
|
23
|
+
export type UseGLTFMaterialVariantsOptions = {
|
|
24
|
+
/**
|
|
25
|
+
* Initial variant to apply on first load. Prevents flashing the first variant
|
|
26
|
+
* before switching. Defaults to the first variant in the model if omitted.
|
|
27
|
+
*/
|
|
28
|
+
variant?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Override for getVariantPromise (testing only). When provided, used instead
|
|
31
|
+
* of the default implementation so tests can avoid suspending.
|
|
32
|
+
*/
|
|
33
|
+
getVariantPromise?: (gltf: GLTFWithVariants, variantName: string) => Promise<{
|
|
34
|
+
variantName: string;
|
|
35
|
+
}>;
|
|
36
|
+
};
|
|
37
|
+
/** Return value of useGLTFMaterialVariants. */
|
|
38
|
+
export type UseGLTFMaterialVariantsResult = {
|
|
39
|
+
/** Variant names from the extension, or empty if no extension. */
|
|
40
|
+
variants: string[];
|
|
41
|
+
/** Currently active variant name, or null if no variants. */
|
|
42
|
+
activeVariant: string | null;
|
|
43
|
+
/** Switch to a variant by name. Suspends until the variant is applied. */
|
|
44
|
+
setVariant: (variantName: string) => void;
|
|
45
|
+
};
|
|
46
|
+
/** Used internally and for testing. */
|
|
47
|
+
export declare function getVariantsExtension(gltf: GLTFWithVariants): VariantsExtension | null;
|
|
48
|
+
/**
|
|
49
|
+
* Parse and assign KHR_materials_variants on an already-loaded glTF.
|
|
50
|
+
* Load the model with useGLTF (Drei), useLoader(GLTFLoader, url), or your own
|
|
51
|
+
* loader; then pass the result here. Pass options.variant for the initial
|
|
52
|
+
* variant to avoid flashing. Call setVariant(name) to switch—both initial
|
|
53
|
+
* apply and setVariant suspend until the variant is applied. Wrap in a React
|
|
54
|
+
* Suspense boundary.
|
|
55
|
+
*
|
|
56
|
+
* @see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_variants
|
|
57
|
+
* @param gltf - Loaded glTF (from useGLTF, useLoader(GLTFLoader, url), etc.)
|
|
58
|
+
* @param options - Optional initial variant (prevents flash on load)
|
|
59
|
+
* @returns variants, activeVariant, and setVariant (render gltf.scene yourself)
|
|
60
|
+
*/
|
|
61
|
+
export declare function useGLTFMaterialVariants(gltf: GLTFWithVariants, options?: UseGLTFMaterialVariantsOptions): UseGLTFMaterialVariantsResult;
|
|
62
|
+
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { CircularCarousel, CircularCarouselNextTrigger, CircularCarouselPrevTrigger, useCarouselContext, LinearCarousel, LinearCarouselNextTrigger, LinearCarouselPrevTrigger, useLinearCarouselContext, } from "./components";
|
|
2
|
+
export { useGLTFMaterialVariants, } from "./hooks";
|
|
2
3
|
export type { CircularCarouselProps, CircularCarouselRef, CarouselContextValue, LinearCarouselProps, LinearCarouselRef, LinearCarouselContextValue, DragConfig, } from "./components";
|
|
4
|
+
export type { GLTFWithVariants, UseGLTFMaterialVariantsOptions, UseGLTFMaterialVariantsResult, } from "./hooks";
|
|
3
5
|
export type { GroupProps } from "./types";
|