@objectifthunes/react-three-book
v0.3.0
Published
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.
Readme
@objectifthunes/react-three-book
A procedural, interactive 3D book for React Three Fiber — drag pages to turn them, apply textures to every surface, and drop <Book> into your R3F scene like any other component.
Features
<Book>R3F component — handles init / update / dispose automatically; rebuilds cleanly via React'skeyprop.<BookInteraction>— declarative pointer-drag wiring for interactive page turning; auto-discovers the book from context.- Hooks —
useBookControls,useAutoTurn,useBookState,useBookContent,usePageTurning. - BookContext — child components access the book instance without prop-drilling.
- Per-surface textures — assign a
THREE.Texture(ornull) to each cover side and page side independently. - Configurable geometry — page/cover width, height, thickness, stiffness, color.
- Texture utilities —
createPageTexture,drawImageWithFit,loadImageincluded.
Installation
npm install @objectifthunes/react-three-book three @react-three/fiber react react-dompnpm add @objectifthunes/react-three-book three @react-three/fiber react react-domPeer dependencies: three >= 0.150.0, react >= 18.0.0, @react-three/fiber >= 8.0.0.
Quick Start
import { Canvas } from '@react-three/fiber';
import { OrbitControls } from '@react-three/drei';
import * as THREE from 'three';
import {
Book,
BookContent,
BookDirection,
BookInteraction,
StapleBookBinding,
useBookContent,
} from '@objectifthunes/react-three-book';
function Scene() {
const orbitRef = useRef(null);
const content = useBookContent(() => {
const c = new BookContent();
c.direction = BookDirection.LeftToRight;
c.covers.push(frontOuterTex, frontInnerTex, backInnerTex, backOuterTex);
c.pages.push(page1Tex, page2Tex, page3Tex, page4Tex);
return c;
}, []);
return (
<>
<OrbitControls ref={orbitRef} />
<Book
content={content}
binding={new StapleBookBinding()}
castShadows
alignToGround
pagePaperSetup={{ width: 2, height: 3, thickness: 0.02, stiffness: 0.2, color: new THREE.Color(1, 1, 1), material: null }}
coverPaperSetup={{ width: 2.1, height: 3.1, thickness: 0.04, stiffness: 0.5, color: new THREE.Color(1, 1, 1), material: null }}
>
<BookInteraction orbitControlsRef={orbitRef} />
</Book>
</>
);
}
export default function App() {
return (
<Canvas shadows camera={{ position: [0, 2, 5], fov: 45 }}>
<Scene />
</Canvas>
);
}Triggering a Rebuild
Change the key prop — React unmounts and remounts <Book>, which runs a clean dispose → init cycle:
<Book key={buildKey} content={content} binding={binding} ... />Imperative Access
Forward a ref to get the underlying ThreeBook instance:
const bookRef = useRef<ThreeBook>(null);
<Book ref={bookRef} ... />
bookRef.current?.setOpenProgress(0.5);Hooks
useBookControls(bookRef?)
const { setOpenProgress, setOpenProgressByIndex, stopTurning } = useBookControls();useAutoTurn(bookRef?)
const { turnNext, turnPrev, turnAll, startAutoTurning, cancelPendingAutoTurns } = useAutoTurn();
turnNext();
turnPrev();
turnAll(AutoTurnDirection.Next);
startAutoTurning(AutoTurnDirection.Next, settings, 5, 0.3);useBookState(bookRef?)
Reactive snapshot updated every frame — triggers re-renders only when something actually changes:
const { isTurning, isIdle, isAutoTurning, paperCount } = useBookState();useBookContent(factory, deps)
Creates a BookContent and disposes its THREE.Textures automatically when deps change or the component unmounts:
const content = useBookContent(() => {
const c = new BookContent();
c.covers.push(myTexture);
c.pages.push(pageA, pageB);
return c;
}, [rebuildKey]);Context
Any component rendered inside <Book> can access the instance without a ref:
function PageButtons() {
const { turnNext, turnPrev } = useAutoTurn();
return (
<>
<button onClick={turnPrev}>◀</button>
<button onClick={turnNext}>▶</button>
</>
);
}
<Book ...>
<BookInteraction />
<PageButtons />
</Book>Texture Utilities
import { createPageTexture, drawImageWithFit, loadImage } from '@objectifthunes/react-three-book';
const tex = createPageTexture('#ff0000', 'Cover', myImage, 'cover', true);
const result = await loadImage(file); // { image, objectUrl } | nullContent Model
Covers — 4 entries, one per surface:
| Index | Surface | |-------|---------| | 0 | Front outer | | 1 | Front inner | | 2 | Back inner | | 3 | Back outer |
Pages — ordered list of page-side textures. Each entry can be a THREE.Texture, an IPageContent implementation, or null (renders the base paper color).
Auto Turn
import { AutoTurnDirection, AutoTurnMode, AutoTurnSettings } from '@objectifthunes/react-three-book';
const { startAutoTurning } = useAutoTurn();
const settings = new AutoTurnSettings();
settings.mode = AutoTurnMode.Edge;
startAutoTurning(AutoTurnDirection.Next, settings, 3);API Surface
| Category | Exports |
|----------|---------|
| Components | Book, BookInteraction |
| Context | BookContext, useBook, useRequiredBook |
| Hooks | useBookRef, useBookContent, useBookControls, useAutoTurn, useBookState, usePageTurning |
| Texture utils | createPageTexture, drawImageWithFit, loadImage |
| Core | ThreeBook, BookContent, BookDirection, Paper, PaperSetup |
| Binding | BookBinding, StapleBookBinding, StapleBookBound, StapleSetup |
| Content | IPageContent, PageContent, SpritePageContent2 |
| Auto turn | AutoTurnDirection, AutoTurnMode, AutoTurnSettings, AutoTurnSetting |
