@objectifthunes/react-three-book 0.1.0 → 0.1.1
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 +214 -0
- package/package.json +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# @objectifthunes/react-three-book
|
|
2
|
+
|
|
3
|
+
A procedural, interactive 3D book for [React Three Fiber](https://docs.pmnd.rs/react-three-fiber) — drag pages to turn them, apply textures to every surface, and drop `<Book>` into your R3F scene like any other component.
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<img src="https://raw.githubusercontent.com/ObjectifThunes/react-three-book/main/docs/images/default.png" width="49%" alt="Closed book" />
|
|
7
|
+
<img src="https://raw.githubusercontent.com/ObjectifThunes/react-three-book/main/docs/images/open-half.png" width="49%" alt="Book opened halfway" />
|
|
8
|
+
</p>
|
|
9
|
+
<p align="center">
|
|
10
|
+
<img src="https://raw.githubusercontent.com/ObjectifThunes/react-three-book/main/docs/images/page-curl.png" width="49%" alt="Page mid-curl" />
|
|
11
|
+
<img src="https://raw.githubusercontent.com/ObjectifThunes/react-three-book/main/docs/images/demo-ui.png" width="49%" alt="Demo app with tweakable controls" />
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
- **`<Book>` R3F component** — handles init / update / dispose automatically; rebuilds cleanly via React's `key` prop.
|
|
17
|
+
- **`<BookInteraction>`** — declarative pointer-drag wiring for interactive page turning; auto-discovers the book from context.
|
|
18
|
+
- **Hooks** — `useBookControls`, `useAutoTurn`, `useBookState`, `useBookContent`, `usePageTurning`.
|
|
19
|
+
- **BookContext** — child components access the book instance without prop-drilling.
|
|
20
|
+
- **Per-surface textures** — assign a `THREE.Texture` (or `null`) to each cover side and page side independently.
|
|
21
|
+
- **Configurable geometry** — page/cover width, height, thickness, stiffness, color.
|
|
22
|
+
- **Texture utilities** — `createPageTexture`, `drawImageWithFit`, `loadImage` included.
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install @objectifthunes/react-three-book three @react-three/fiber react react-dom
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pnpm add @objectifthunes/react-three-book three @react-three/fiber react react-dom
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Peer dependencies: `three >= 0.150.0`, `react >= 18.0.0`, `@react-three/fiber >= 8.0.0`.
|
|
35
|
+
|
|
36
|
+
## Quick Start
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
import { Canvas } from '@react-three/fiber';
|
|
40
|
+
import { OrbitControls } from '@react-three/drei';
|
|
41
|
+
import * as THREE from 'three';
|
|
42
|
+
import {
|
|
43
|
+
Book,
|
|
44
|
+
BookContent,
|
|
45
|
+
BookDirection,
|
|
46
|
+
BookInteraction,
|
|
47
|
+
StapleBookBinding,
|
|
48
|
+
useBookContent,
|
|
49
|
+
} from '@objectifthunes/react-three-book';
|
|
50
|
+
|
|
51
|
+
function Scene() {
|
|
52
|
+
const orbitRef = useRef(null);
|
|
53
|
+
|
|
54
|
+
const content = useBookContent(() => {
|
|
55
|
+
const c = new BookContent();
|
|
56
|
+
c.direction = BookDirection.LeftToRight;
|
|
57
|
+
c.covers.push(frontOuterTex, frontInnerTex, backInnerTex, backOuterTex);
|
|
58
|
+
c.pages.push(page1Tex, page2Tex, page3Tex, page4Tex);
|
|
59
|
+
return c;
|
|
60
|
+
}, []);
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<>
|
|
64
|
+
<OrbitControls ref={orbitRef} />
|
|
65
|
+
<Book
|
|
66
|
+
content={content}
|
|
67
|
+
binding={new StapleBookBinding()}
|
|
68
|
+
castShadows
|
|
69
|
+
alignToGround
|
|
70
|
+
pagePaperSetup={{ width: 2, height: 3, thickness: 0.02, stiffness: 0.2, color: new THREE.Color(1, 1, 1), material: null }}
|
|
71
|
+
coverPaperSetup={{ width: 2.1, height: 3.1, thickness: 0.04, stiffness: 0.5, color: new THREE.Color(1, 1, 1), material: null }}
|
|
72
|
+
>
|
|
73
|
+
<BookInteraction orbitControlsRef={orbitRef} />
|
|
74
|
+
</Book>
|
|
75
|
+
</>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export default function App() {
|
|
80
|
+
return (
|
|
81
|
+
<Canvas shadows camera={{ position: [0, 2, 5], fov: 45 }}>
|
|
82
|
+
<Scene />
|
|
83
|
+
</Canvas>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Triggering a Rebuild
|
|
89
|
+
|
|
90
|
+
Change the `key` prop — React unmounts and remounts `<Book>`, which runs a clean dispose → init cycle:
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
<Book key={buildKey} content={content} binding={binding} ... />
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Imperative Access
|
|
97
|
+
|
|
98
|
+
Forward a ref to get the underlying `ThreeBook` instance:
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
const bookRef = useRef<ThreeBook>(null);
|
|
102
|
+
<Book ref={bookRef} ... />
|
|
103
|
+
|
|
104
|
+
bookRef.current?.setOpenProgress(0.5);
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Hooks
|
|
108
|
+
|
|
109
|
+
### `useBookControls(bookRef?)`
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
const { setOpenProgress, setOpenProgressByIndex, stopTurning } = useBookControls();
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### `useAutoTurn(bookRef?)`
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
const { turnNext, turnPrev, turnAll, startAutoTurning, cancelPendingAutoTurns } = useAutoTurn();
|
|
119
|
+
|
|
120
|
+
turnNext();
|
|
121
|
+
turnPrev();
|
|
122
|
+
turnAll(AutoTurnDirection.Next);
|
|
123
|
+
startAutoTurning(AutoTurnDirection.Next, settings, 5, 0.3);
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### `useBookState(bookRef?)`
|
|
127
|
+
|
|
128
|
+
Reactive snapshot updated every frame — triggers re-renders only when something actually changes:
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
const { isTurning, isIdle, isAutoTurning, paperCount } = useBookState();
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### `useBookContent(factory, deps)`
|
|
135
|
+
|
|
136
|
+
Creates a `BookContent` and disposes its `THREE.Texture`s automatically when `deps` change or the component unmounts:
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
const content = useBookContent(() => {
|
|
140
|
+
const c = new BookContent();
|
|
141
|
+
c.covers.push(myTexture);
|
|
142
|
+
c.pages.push(pageA, pageB);
|
|
143
|
+
return c;
|
|
144
|
+
}, [rebuildKey]);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Context
|
|
148
|
+
|
|
149
|
+
Any component rendered inside `<Book>` can access the instance without a ref:
|
|
150
|
+
|
|
151
|
+
```tsx
|
|
152
|
+
function PageButtons() {
|
|
153
|
+
const { turnNext, turnPrev } = useAutoTurn();
|
|
154
|
+
return (
|
|
155
|
+
<>
|
|
156
|
+
<button onClick={turnPrev}>◀</button>
|
|
157
|
+
<button onClick={turnNext}>▶</button>
|
|
158
|
+
</>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
<Book ...>
|
|
163
|
+
<BookInteraction />
|
|
164
|
+
<PageButtons />
|
|
165
|
+
</Book>
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Texture Utilities
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
import { createPageTexture, drawImageWithFit, loadImage } from '@objectifthunes/react-three-book';
|
|
172
|
+
|
|
173
|
+
const tex = createPageTexture('#ff0000', 'Cover', myImage, 'cover', true);
|
|
174
|
+
|
|
175
|
+
const result = await loadImage(file); // { image, objectUrl } | null
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Content Model
|
|
179
|
+
|
|
180
|
+
**Covers** — 4 entries, one per surface:
|
|
181
|
+
|
|
182
|
+
| Index | Surface |
|
|
183
|
+
|-------|---------|
|
|
184
|
+
| 0 | Front outer |
|
|
185
|
+
| 1 | Front inner |
|
|
186
|
+
| 2 | Back inner |
|
|
187
|
+
| 3 | Back outer |
|
|
188
|
+
|
|
189
|
+
**Pages** — ordered list of page-side textures. Each entry can be a `THREE.Texture`, an `IPageContent` implementation, or `null` (renders the base paper color).
|
|
190
|
+
|
|
191
|
+
## Auto Turn
|
|
192
|
+
|
|
193
|
+
```tsx
|
|
194
|
+
import { AutoTurnDirection, AutoTurnMode, AutoTurnSettings } from '@objectifthunes/react-three-book';
|
|
195
|
+
|
|
196
|
+
const { startAutoTurning } = useAutoTurn();
|
|
197
|
+
|
|
198
|
+
const settings = new AutoTurnSettings();
|
|
199
|
+
settings.mode = AutoTurnMode.Edge;
|
|
200
|
+
startAutoTurning(AutoTurnDirection.Next, settings, 3);
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## API Surface
|
|
204
|
+
|
|
205
|
+
| Category | Exports |
|
|
206
|
+
|----------|---------|
|
|
207
|
+
| Components | `Book`, `BookInteraction` |
|
|
208
|
+
| Context | `BookContext`, `useBook`, `useRequiredBook` |
|
|
209
|
+
| Hooks | `useBookRef`, `useBookContent`, `useBookControls`, `useAutoTurn`, `useBookState`, `usePageTurning` |
|
|
210
|
+
| Texture utils | `createPageTexture`, `drawImageWithFit`, `loadImage` |
|
|
211
|
+
| Core | `ThreeBook`, `BookContent`, `BookDirection`, `Paper`, `PaperSetup` |
|
|
212
|
+
| Binding | `BookBinding`, `StapleBookBinding`, `StapleBookBound`, `StapleSetup` |
|
|
213
|
+
| Content | `IPageContent`, `PageContent`, `SpritePageContent2` |
|
|
214
|
+
| Auto turn | `AutoTurnDirection`, `AutoTurnMode`, `AutoTurnSettings`, `AutoTurnSetting` |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectifthunes/react-three-book",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"types": "./dist/index.d.ts"
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
|
-
"files": ["dist"],
|
|
13
|
+
"files": ["dist", "README.md"],
|
|
14
14
|
"scripts": {
|
|
15
15
|
"build": "vite build",
|
|
16
16
|
"typecheck": "tsc --noEmit",
|