react-3d-flipbook 1.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/LICENSE +21 -0
- package/README.md +1005 -0
- package/dist/components/BookmarksPanel.d.ts +21 -0
- package/dist/components/Flipbook.d.ts +5 -0
- package/dist/components/SearchPanel.d.ts +26 -0
- package/dist/components/TableOfContents.d.ts +13 -0
- package/dist/components/ThumbnailsPanel.d.ts +14 -0
- package/dist/components/WebGLPageFlip.d.ts +46 -0
- package/dist/components/index.d.ts +12 -0
- package/dist/hooks/index.d.ts +5 -0
- package/dist/hooks/useAutoplay.d.ts +58 -0
- package/dist/hooks/useFlipbook.d.ts +48 -0
- package/dist/hooks/usePdfLoader.d.ts +127 -0
- package/dist/index.d.ts +1121 -0
- package/dist/index.esm.js +25056 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +25121 -0
- package/dist/index.js.map +1 -0
- package/dist/setupTests.d.ts +1 -0
- package/dist/types/index.d.ts +481 -0
- package/dist/utils/index.d.ts +131 -0
- package/dist/utils/pdfUtils.d.ts +158 -0
- package/package.json +122 -0
package/README.md
ADDED
|
@@ -0,0 +1,1005 @@
|
|
|
1
|
+
# React 3D Flipbook
|
|
2
|
+
|
|
3
|
+
A modern, feature-rich React library for creating beautiful, interactive 3D flipbooks with realistic page-turning effects powered by WebGL and Three.js.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- 🎮 **WebGL 3D Rendering**: Realistic page-flip animations with Three.js
|
|
10
|
+
- 📄 **PDF Support**: Direct PDF rendering with automatic page sizing via PDF.js
|
|
11
|
+
- 📐 **Dynamic Page Sizing**: Automatically adapts to PDF page dimensions
|
|
12
|
+
- 💡 **Dynamic Lighting**: Configurable lights and shadows for depth
|
|
13
|
+
- 📱 **Responsive Design**: Works seamlessly on desktop, tablet, and mobile devices
|
|
14
|
+
- 🔍 **Zoom Controls**: Pinch-to-zoom, mouse wheel, and button controls
|
|
15
|
+
- 🔖 **Bookmarks**: Save, manage, and persist bookmarked pages
|
|
16
|
+
- 📑 **Table of Contents**: Hierarchical, searchable TOC navigation
|
|
17
|
+
- 🖼️ **Thumbnails Panel**: Visual page thumbnail navigation
|
|
18
|
+
- 🔎 **Full-Text Search**: Search across all pages with highlighted results
|
|
19
|
+
- ▶️ **Autoplay**: Automatic page flipping with customizable intervals
|
|
20
|
+
- 🌙 **Themes**: Light, dark, and gradient themes with full customization
|
|
21
|
+
- ♿ **Accessible**: ARIA labels, screen reader support, and keyboard navigation
|
|
22
|
+
- 📦 **TypeScript**: Complete TypeScript definitions for type safety
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
npm install react-3d-flipbook
|
|
30
|
+
# or
|
|
31
|
+
yarn add react-3d-flipbook
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Required Peer Dependencies
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
npm install three @types/three
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Optional (for PDF support)
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
npm install pdfjs-dist
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Quick Start
|
|
49
|
+
|
|
50
|
+
### Basic Usage with Images
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
import { Flipbook } from 'react-3d-flipbook';
|
|
54
|
+
import 'react-3d-flipbook/dist/styles.css';
|
|
55
|
+
|
|
56
|
+
const pages = [
|
|
57
|
+
{ src: '/images/page1.jpg', title: 'Cover' },
|
|
58
|
+
{ src: '/images/page2.jpg', title: 'Introduction' },
|
|
59
|
+
// ...more pages
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
function App() {
|
|
63
|
+
return (
|
|
64
|
+
<Flipbook
|
|
65
|
+
pages={pages}
|
|
66
|
+
width={800}
|
|
67
|
+
height={600}
|
|
68
|
+
onPageFlip={e => console.log('Flipped to page', e.page)}
|
|
69
|
+
/>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### PDF Support
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
import { useState, useEffect } from 'react';
|
|
78
|
+
import { Flipbook, pdfToFlipbookPages, setPdfWorkerSrc } from 'react-3d-flipbook';
|
|
79
|
+
import 'react-3d-flipbook/dist/styles.css';
|
|
80
|
+
|
|
81
|
+
// Set up PDF.js worker (required once)
|
|
82
|
+
setPdfWorkerSrc('https://unpkg.com/pdfjs-dist@4.0.379/build/pdf.worker.mjs');
|
|
83
|
+
|
|
84
|
+
function PdfFlipbook() {
|
|
85
|
+
const [pages, setPages] = useState([]);
|
|
86
|
+
const [loading, setLoading] = useState(true);
|
|
87
|
+
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
async function loadPdf() {
|
|
90
|
+
const loadedPages = await pdfToFlipbookPages('/path/to/document.pdf', {
|
|
91
|
+
scale: 1.5,
|
|
92
|
+
format: 'jpeg',
|
|
93
|
+
quality: 0.8,
|
|
94
|
+
onProgress: (current, total) => console.log(`Loading ${current}/${total}`)
|
|
95
|
+
});
|
|
96
|
+
setPages(loadedPages);
|
|
97
|
+
setLoading(false);
|
|
98
|
+
}
|
|
99
|
+
loadPdf();
|
|
100
|
+
}, []);
|
|
101
|
+
|
|
102
|
+
if (loading) return <div>Loading PDF...</div>;
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<Flipbook
|
|
106
|
+
pages={pages}
|
|
107
|
+
width={800}
|
|
108
|
+
height={600}
|
|
109
|
+
singlePageMode={false}
|
|
110
|
+
/>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### PDF with Auto-Sizing
|
|
116
|
+
|
|
117
|
+
The flipbook automatically sizes pages based on the PDF's actual dimensions:
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
import {
|
|
121
|
+
Flipbook,
|
|
122
|
+
pdfToFlipbookPages,
|
|
123
|
+
calculateFlipbookSizeFromPages,
|
|
124
|
+
setPdfWorkerSrc
|
|
125
|
+
} from 'react-3d-flipbook';
|
|
126
|
+
|
|
127
|
+
setPdfWorkerSrc('https://unpkg.com/pdfjs-dist@4.0.379/build/pdf.worker.mjs');
|
|
128
|
+
|
|
129
|
+
function AutoSizedPdfFlipbook() {
|
|
130
|
+
const [pages, setPages] = useState([]);
|
|
131
|
+
const [size, setSize] = useState({ width: 800, height: 600 });
|
|
132
|
+
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
async function loadPdf() {
|
|
135
|
+
const loadedPages = await pdfToFlipbookPages('/document.pdf');
|
|
136
|
+
setPages(loadedPages);
|
|
137
|
+
|
|
138
|
+
// Calculate optimal container size based on PDF page dimensions
|
|
139
|
+
const calculatedSize = calculateFlipbookSizeFromPages(loadedPages, {
|
|
140
|
+
singlePageMode: false,
|
|
141
|
+
maxWidth: 1200,
|
|
142
|
+
maxHeight: 800,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
if (calculatedSize) {
|
|
146
|
+
setSize(calculatedSize);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
loadPdf();
|
|
150
|
+
}, []);
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<Flipbook
|
|
154
|
+
pages={pages}
|
|
155
|
+
width={size.width}
|
|
156
|
+
height={size.height}
|
|
157
|
+
/>
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## PDF Utilities
|
|
165
|
+
|
|
166
|
+
### `setPdfWorkerSrc(workerSrc: string)`
|
|
167
|
+
|
|
168
|
+
Set the PDF.js worker source URL. Must be called before using any PDF functions.
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
import { setPdfWorkerSrc } from 'react-3d-flipbook';
|
|
172
|
+
|
|
173
|
+
// Using CDN
|
|
174
|
+
setPdfWorkerSrc('https://unpkg.com/pdfjs-dist@4.0.379/build/pdf.worker.mjs');
|
|
175
|
+
|
|
176
|
+
// Using local file (Vite)
|
|
177
|
+
import workerSrc from 'pdfjs-dist/build/pdf.worker.mjs?url';
|
|
178
|
+
setPdfWorkerSrc(workerSrc);
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### `pdfToFlipbookPages(source, options)`
|
|
182
|
+
|
|
183
|
+
Convert a PDF to flipbook pages.
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
import { pdfToFlipbookPages } from 'react-3d-flipbook';
|
|
187
|
+
|
|
188
|
+
const pages = await pdfToFlipbookPages('/document.pdf', {
|
|
189
|
+
scale: 2, // Render scale (default: 2)
|
|
190
|
+
format: 'jpeg', // 'png' or 'jpeg' (default: 'png')
|
|
191
|
+
quality: 0.92, // JPEG quality 0-1 (default: 0.92)
|
|
192
|
+
pageNumbers: [1, 2, 5], // Specific pages to render (optional)
|
|
193
|
+
onProgress: (current, total) => console.log(`${current}/${total}`)
|
|
194
|
+
});
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Returns:** `Promise<FlipbookPage[]>` - Array of pages with `src`, `width`, `height`, and `orientation`.
|
|
198
|
+
|
|
199
|
+
### `pdfToFlipbookPagesLazy(source, options)`
|
|
200
|
+
|
|
201
|
+
Lazy/streaming PDF page loading for memory efficiency.
|
|
202
|
+
|
|
203
|
+
```tsx
|
|
204
|
+
import { pdfToFlipbookPagesLazy } from 'react-3d-flipbook';
|
|
205
|
+
|
|
206
|
+
const pages = [];
|
|
207
|
+
for await (const page of pdfToFlipbookPagesLazy('/large-document.pdf')) {
|
|
208
|
+
pages.push(page);
|
|
209
|
+
// Update UI progressively
|
|
210
|
+
setPages([...pages]);
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### `getPdfInfo(source)`
|
|
215
|
+
|
|
216
|
+
Get PDF document info without rendering pages.
|
|
217
|
+
|
|
218
|
+
```tsx
|
|
219
|
+
import { getPdfInfo } from 'react-3d-flipbook';
|
|
220
|
+
|
|
221
|
+
const info = await getPdfInfo('/document.pdf');
|
|
222
|
+
console.log(info.numPages); // Total pages
|
|
223
|
+
console.log(info.pageInfos); // Array of { pageNumber, width, height, orientation }
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### `calculateFlipbookSize(pageWidth, pageHeight, options)`
|
|
227
|
+
|
|
228
|
+
Calculate optimal flipbook container size based on page dimensions.
|
|
229
|
+
|
|
230
|
+
```tsx
|
|
231
|
+
import { calculateFlipbookSize } from 'react-3d-flipbook';
|
|
232
|
+
|
|
233
|
+
const size = calculateFlipbookSize(595, 842, { // A4 dimensions
|
|
234
|
+
singlePageMode: false,
|
|
235
|
+
maxWidth: 1200,
|
|
236
|
+
maxHeight: 800,
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
console.log(size.width); // Calculated container width
|
|
240
|
+
console.log(size.height); // Calculated container height
|
|
241
|
+
console.log(size.aspectRatio); // Container aspect ratio
|
|
242
|
+
console.log(size.isLandscape); // Whether page is landscape
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### `calculateFlipbookSizeFromPages(pages, options)`
|
|
246
|
+
|
|
247
|
+
Calculate optimal size from a FlipbookPage array using the first page's dimensions.
|
|
248
|
+
|
|
249
|
+
```tsx
|
|
250
|
+
import { calculateFlipbookSizeFromPages } from 'react-3d-flipbook';
|
|
251
|
+
|
|
252
|
+
const pages = await pdfToFlipbookPages('/document.pdf');
|
|
253
|
+
const size = calculateFlipbookSizeFromPages(pages, {
|
|
254
|
+
singlePageMode: true,
|
|
255
|
+
maxWidth: 1000,
|
|
256
|
+
maxHeight: 700,
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
if (size) {
|
|
260
|
+
console.log(`Container: ${size.width}x${size.height}`);
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## API Reference
|
|
267
|
+
|
|
268
|
+
### `<Flipbook />` Props
|
|
269
|
+
|
|
270
|
+
| Prop | Type | Default | Description |
|
|
271
|
+
|------------------------|--------------------------------------|------------------------|----------------------------------------------|
|
|
272
|
+
| `pages` | `FlipbookPage[]` | **Required** | Array of page objects |
|
|
273
|
+
| `width` | `string \| number` | `'100%'` | Container width |
|
|
274
|
+
| `height` | `string \| number` | `'600px'` | Container height |
|
|
275
|
+
| `skin` | `'dark' \| 'light' \| 'gradient'` | `'dark'` | Theme/skin |
|
|
276
|
+
| `backgroundColor` | `string` | `'rgb(81,85,88)'` | Background color |
|
|
277
|
+
| `startPage` | `number` | `1` | Initial page |
|
|
278
|
+
| `rightToLeft` | `boolean` | `false` | RTL reading direction |
|
|
279
|
+
| `singlePageMode` | `boolean` | `false` | Show one page at a time |
|
|
280
|
+
| `sideNavigationButtons`| `boolean` | `true` | Show side nav buttons |
|
|
281
|
+
| `hideMenu` | `boolean` | `false` | Hide the toolbar |
|
|
282
|
+
| `zoomMin` | `number` | `1` | Minimum zoom level |
|
|
283
|
+
| `zoomMax2` | `number` | `3` | Maximum zoom level |
|
|
284
|
+
| `zoomStep` | `number` | `0.5` | Zoom increment |
|
|
285
|
+
| `touchSwipeEnabled` | `boolean` | `true` | Enable touch swipe |
|
|
286
|
+
| `tableOfContent` | `TocItem[]` | `undefined` | Table of contents data |
|
|
287
|
+
|
|
288
|
+
#### WebGL 3D Props
|
|
289
|
+
|
|
290
|
+
| Prop | Type | Default | Description |
|
|
291
|
+
|-------------------|-----------|---------|-----------------------------------|
|
|
292
|
+
| `pageFlipDuration`| `number` | `600` | Flip animation duration (ms) |
|
|
293
|
+
| `pageHardness` | `number` | `0.5` | Page flexibility (0-1) |
|
|
294
|
+
| `coverHardness` | `number` | `0.9` | Cover rigidity (0-1) |
|
|
295
|
+
| `shadows` | `boolean` | `true` | Enable shadows |
|
|
296
|
+
| `shadowOpacity` | `number` | `0.3` | Shadow intensity (0-1) |
|
|
297
|
+
| `lights` | `boolean` | `true` | Enable dynamic lighting |
|
|
298
|
+
| `lightIntensity` | `number` | `1` | Light brightness |
|
|
299
|
+
| `pageRoughness` | `number` | `0.8` | Material roughness (0-1) |
|
|
300
|
+
| `pageMetalness` | `number` | `0.1` | Material metalness (0-1) |
|
|
301
|
+
| `antialias` | `boolean` | `true` | Enable antialiasing |
|
|
302
|
+
|
|
303
|
+
#### Camera & Display Props
|
|
304
|
+
|
|
305
|
+
| Prop | Type | Default | Description |
|
|
306
|
+
|-------------------|-----------|---------|----------------------------------------------------------|
|
|
307
|
+
| `cameraZoom` | `number` | `1.35` | Camera zoom/margin factor - higher moves camera back |
|
|
308
|
+
| `pageScale` | `number` | `6` | Base page scale in world units - affects page size |
|
|
309
|
+
| `cameraPositionY` | `number` | `0` | Camera vertical position offset |
|
|
310
|
+
| `cameraLookAtY` | `number` | `0` | Camera look-at Y position |
|
|
311
|
+
| `cameraFov` | `number` | `45` | Field of view in degrees |
|
|
312
|
+
|
|
313
|
+
**Usage Example:**
|
|
314
|
+
|
|
315
|
+
```tsx
|
|
316
|
+
// Zoom out to show more padding around the book
|
|
317
|
+
<Flipbook
|
|
318
|
+
pages={pages}
|
|
319
|
+
width={800}
|
|
320
|
+
height={600}
|
|
321
|
+
cameraZoom={1.5} // Further back (default: 1.35)
|
|
322
|
+
/>
|
|
323
|
+
|
|
324
|
+
// Zoom in to fill more of the container
|
|
325
|
+
<Flipbook
|
|
326
|
+
pages={pages}
|
|
327
|
+
width={800}
|
|
328
|
+
height={600}
|
|
329
|
+
cameraZoom={1.1} // Closer (less padding)
|
|
330
|
+
/>
|
|
331
|
+
|
|
332
|
+
// Adjust camera angle for a different perspective
|
|
333
|
+
<Flipbook
|
|
334
|
+
pages={pages}
|
|
335
|
+
width={800}
|
|
336
|
+
height={600}
|
|
337
|
+
cameraPositionY={-1} // Camera slightly below center
|
|
338
|
+
cameraLookAtY={0.5} // Looking slightly upward
|
|
339
|
+
/>
|
|
340
|
+
|
|
341
|
+
// Make pages larger in the scene
|
|
342
|
+
<Flipbook
|
|
343
|
+
pages={pages}
|
|
344
|
+
width={800}
|
|
345
|
+
height={600}
|
|
346
|
+
pageScale={8} // Larger pages (default: 6)
|
|
347
|
+
/>
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
#### Event Callbacks
|
|
351
|
+
|
|
352
|
+
| Prop | Type | Description |
|
|
353
|
+
|------------------------|-----------------------------------------|---------------------------------------------|
|
|
354
|
+
| `onPageFlip` | `(e: { page, direction }) => void` | Called when page changes |
|
|
355
|
+
| `onPageFlipStart` | `(e: { page, direction }) => void` | Called when flip starts |
|
|
356
|
+
| `onPageFlipEnd` | `(e: { page }) => void` | Called when flip ends |
|
|
357
|
+
| `onZoomChange` | `(e: { zoom }) => void` | Called when zoom changes |
|
|
358
|
+
| `onReady` | `() => void` | Called when flipbook is ready |
|
|
359
|
+
| `onFullscreenChange` | `(e: { isFullscreen }) => void` | Called on fullscreen toggle |
|
|
360
|
+
| `onLoadProgress` | `(e: { progress }) => void` | Called during loading |
|
|
361
|
+
|
|
362
|
+
### `FlipbookPage` Type
|
|
363
|
+
|
|
364
|
+
```ts
|
|
365
|
+
interface FlipbookPage {
|
|
366
|
+
src: string; // Image source URL (required)
|
|
367
|
+
thumb?: string; // Thumbnail image URL
|
|
368
|
+
title?: string; // Page title for TOC
|
|
369
|
+
htmlContent?: string; // HTML content overlay
|
|
370
|
+
empty?: boolean; // Whether this is a blank page
|
|
371
|
+
width?: number; // Page width in pixels (for PDF pages)
|
|
372
|
+
height?: number; // Page height in pixels (for PDF pages)
|
|
373
|
+
orientation?: 'portrait' | 'landscape'; // Page orientation
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## Display Modes
|
|
380
|
+
|
|
381
|
+
### Two-Page Spread Mode (Default)
|
|
382
|
+
|
|
383
|
+
Displays two pages side by side like an open book. Pages flip from right to left with a realistic 3D curl animation.
|
|
384
|
+
|
|
385
|
+
```tsx
|
|
386
|
+
<Flipbook
|
|
387
|
+
pages={pages}
|
|
388
|
+
width={1200}
|
|
389
|
+
height={800}
|
|
390
|
+
singlePageMode={false}
|
|
391
|
+
/>
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### Single-Page Mode
|
|
395
|
+
|
|
396
|
+
Displays one page at a time with a fly-out animation when navigating.
|
|
397
|
+
|
|
398
|
+
```tsx
|
|
399
|
+
<Flipbook
|
|
400
|
+
pages={pages}
|
|
401
|
+
width={600}
|
|
402
|
+
height={800}
|
|
403
|
+
singlePageMode={true}
|
|
404
|
+
/>
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
## Dynamic Page Sizing
|
|
410
|
+
|
|
411
|
+
The flipbook automatically handles PDFs with varying page sizes:
|
|
412
|
+
|
|
413
|
+
- **Consistent sizing**: All pages are scaled to match the first page's aspect ratio for a uniform appearance
|
|
414
|
+
- **Automatic camera adjustment**: The camera distance adjusts to frame the content properly
|
|
415
|
+
- **Portrait & Landscape support**: Works with both portrait and landscape PDFs
|
|
416
|
+
|
|
417
|
+
### How It Works
|
|
418
|
+
|
|
419
|
+
1. When loading a PDF, each page's `width` and `height` are stored in the `FlipbookPage` object
|
|
420
|
+
2. The flipbook uses the first page's dimensions as the reference for all pages
|
|
421
|
+
3. The camera automatically calculates the optimal distance to display the content with appropriate margins
|
|
422
|
+
4. In two-page mode, the spread width is doubled for proper framing
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
## Panels
|
|
427
|
+
|
|
428
|
+
### Thumbnails Panel
|
|
429
|
+
|
|
430
|
+
```tsx
|
|
431
|
+
import { ThumbnailsPanel } from 'react-3d-flipbook';
|
|
432
|
+
|
|
433
|
+
<ThumbnailsPanel
|
|
434
|
+
pages={pages}
|
|
435
|
+
currentPage={currentPage}
|
|
436
|
+
isOpen={showThumbnails}
|
|
437
|
+
position="left"
|
|
438
|
+
thumbnailSize={120}
|
|
439
|
+
showPageNumbers={true}
|
|
440
|
+
onPageSelect={goToPage}
|
|
441
|
+
onClose={() => setShowThumbnails(false)}
|
|
442
|
+
/>
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### Table of Contents
|
|
446
|
+
|
|
447
|
+
```tsx
|
|
448
|
+
import { TableOfContents, TocItem } from 'react-3d-flipbook';
|
|
449
|
+
|
|
450
|
+
const tocItems: TocItem[] = [
|
|
451
|
+
{
|
|
452
|
+
title: 'Chapter 1',
|
|
453
|
+
page: 1,
|
|
454
|
+
children: [
|
|
455
|
+
{ title: 'Section 1.1', page: 2 },
|
|
456
|
+
{ title: 'Section 1.2', page: 5 },
|
|
457
|
+
],
|
|
458
|
+
},
|
|
459
|
+
{ title: 'Chapter 2', page: 10 },
|
|
460
|
+
];
|
|
461
|
+
|
|
462
|
+
<TableOfContents
|
|
463
|
+
items={tocItems}
|
|
464
|
+
currentPage={currentPage}
|
|
465
|
+
isOpen={showToc}
|
|
466
|
+
position="left"
|
|
467
|
+
closeOnClick={true}
|
|
468
|
+
onPageSelect={goToPage}
|
|
469
|
+
onClose={() => setShowToc(false)}
|
|
470
|
+
/>
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
### Search Panel
|
|
474
|
+
|
|
475
|
+
```tsx
|
|
476
|
+
import { SearchPanel } from 'react-3d-flipbook';
|
|
477
|
+
|
|
478
|
+
<SearchPanel
|
|
479
|
+
pages={pages}
|
|
480
|
+
pageTextContent={textContentMap}
|
|
481
|
+
currentPage={currentPage}
|
|
482
|
+
isOpen={showSearch}
|
|
483
|
+
position="left"
|
|
484
|
+
minQueryLength={2}
|
|
485
|
+
maxResults={100}
|
|
486
|
+
highlightColor="#ffeb3b"
|
|
487
|
+
onPageSelect={goToPage}
|
|
488
|
+
onClose={() => setShowSearch(false)}
|
|
489
|
+
/>
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
### Bookmarks Panel
|
|
493
|
+
|
|
494
|
+
```tsx
|
|
495
|
+
import { BookmarksPanel } from 'react-3d-flipbook';
|
|
496
|
+
|
|
497
|
+
<BookmarksPanel
|
|
498
|
+
pages={pages}
|
|
499
|
+
bookmarks={bookmarkedPages}
|
|
500
|
+
currentPage={currentPage}
|
|
501
|
+
isOpen={showBookmarks}
|
|
502
|
+
position="left"
|
|
503
|
+
showThumbnails={true}
|
|
504
|
+
onPageSelect={goToPage}
|
|
505
|
+
onRemoveBookmark={removeBookmark}
|
|
506
|
+
onClose={() => setShowBookmarks(false)}
|
|
507
|
+
/>
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
---
|
|
511
|
+
|
|
512
|
+
## Hooks
|
|
513
|
+
|
|
514
|
+
### useFlipbook
|
|
515
|
+
|
|
516
|
+
```tsx
|
|
517
|
+
import { useFlipbook } from 'react-3d-flipbook';
|
|
518
|
+
|
|
519
|
+
function CustomFlipbook({ pages }) {
|
|
520
|
+
const {
|
|
521
|
+
currentPage,
|
|
522
|
+
numPages,
|
|
523
|
+
zoom,
|
|
524
|
+
isFullscreen,
|
|
525
|
+
isLoading,
|
|
526
|
+
canGoNext,
|
|
527
|
+
canGoPrev,
|
|
528
|
+
bookmarkedPages,
|
|
529
|
+
containerRef,
|
|
530
|
+
nextPage,
|
|
531
|
+
prevPage,
|
|
532
|
+
firstPage,
|
|
533
|
+
lastPage,
|
|
534
|
+
goToPage,
|
|
535
|
+
zoomIn,
|
|
536
|
+
zoomOut,
|
|
537
|
+
zoomTo,
|
|
538
|
+
toggleFullscreen,
|
|
539
|
+
toggleBookmark,
|
|
540
|
+
isPageBookmarked,
|
|
541
|
+
} = useFlipbook({
|
|
542
|
+
pages,
|
|
543
|
+
initialPage: 1,
|
|
544
|
+
zoomMin: 1,
|
|
545
|
+
zoomMax2: 3,
|
|
546
|
+
onPageChange: (page) => console.log('Page:', page),
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
return (
|
|
550
|
+
<div ref={containerRef}>
|
|
551
|
+
{/* Your custom UI */}
|
|
552
|
+
</div>
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
### useAutoplay
|
|
558
|
+
|
|
559
|
+
```tsx
|
|
560
|
+
import { useAutoplay } from 'react-3d-flipbook';
|
|
561
|
+
|
|
562
|
+
function AutoplayFlipbook({ pages }) {
|
|
563
|
+
const [currentPage, setCurrentPage] = useState(1);
|
|
564
|
+
|
|
565
|
+
const {
|
|
566
|
+
isPlaying,
|
|
567
|
+
isPaused,
|
|
568
|
+
interval,
|
|
569
|
+
progress,
|
|
570
|
+
start,
|
|
571
|
+
stop,
|
|
572
|
+
pause,
|
|
573
|
+
resume,
|
|
574
|
+
toggle,
|
|
575
|
+
setInterval,
|
|
576
|
+
skipNext,
|
|
577
|
+
reset,
|
|
578
|
+
} = useAutoplay({
|
|
579
|
+
numPages: pages.length,
|
|
580
|
+
currentPage,
|
|
581
|
+
interval: 3000,
|
|
582
|
+
loop: true,
|
|
583
|
+
autoStart: false,
|
|
584
|
+
pageIncrement: 2,
|
|
585
|
+
onPageChange: (page) => setCurrentPage(page),
|
|
586
|
+
onComplete: () => console.log('Autoplay completed'),
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
return (
|
|
590
|
+
<div>
|
|
591
|
+
<button onClick={toggle}>
|
|
592
|
+
{isPlaying ? (isPaused ? 'Resume' : 'Pause') : 'Play'}
|
|
593
|
+
</button>
|
|
594
|
+
<progress value={progress} max={1} />
|
|
595
|
+
</div>
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
### usePdfLoader
|
|
601
|
+
|
|
602
|
+
```tsx
|
|
603
|
+
import { usePdfLoader } from 'react-3d-flipbook';
|
|
604
|
+
|
|
605
|
+
function PdfViewer() {
|
|
606
|
+
const {
|
|
607
|
+
isAvailable,
|
|
608
|
+
isLoading,
|
|
609
|
+
progress,
|
|
610
|
+
error,
|
|
611
|
+
numPages,
|
|
612
|
+
pages,
|
|
613
|
+
textContent,
|
|
614
|
+
loadPdf,
|
|
615
|
+
renderPage,
|
|
616
|
+
getPageText,
|
|
617
|
+
destroy,
|
|
618
|
+
} = usePdfLoader({
|
|
619
|
+
source: '/path/to/document.pdf',
|
|
620
|
+
workerSrc: '/pdf.worker.min.js',
|
|
621
|
+
scale: 1.5,
|
|
622
|
+
extractText: true,
|
|
623
|
+
onLoadProgress: (p) => console.log(`Loading: ${p * 100}%`),
|
|
624
|
+
onLoadComplete: (n) => console.log(`Loaded ${n} pages`),
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
if (isLoading) {
|
|
628
|
+
return <div>Loading... {Math.round(progress * 100)}%</div>;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
return <Flipbook pages={pages} />;
|
|
632
|
+
}
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
### useSwipeGesture
|
|
636
|
+
|
|
637
|
+
```tsx
|
|
638
|
+
import { useSwipeGesture } from 'react-3d-flipbook';
|
|
639
|
+
|
|
640
|
+
function SwipeableComponent() {
|
|
641
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
642
|
+
|
|
643
|
+
useSwipeGesture(
|
|
644
|
+
ref,
|
|
645
|
+
() => console.log('Swiped left'),
|
|
646
|
+
() => console.log('Swiped right'),
|
|
647
|
+
{ threshold: 50, enabled: true }
|
|
648
|
+
);
|
|
649
|
+
|
|
650
|
+
return <div ref={ref}>Swipe me!</div>;
|
|
651
|
+
}
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
### useWheelZoom
|
|
655
|
+
|
|
656
|
+
```tsx
|
|
657
|
+
import { useWheelZoom } from 'react-3d-flipbook';
|
|
658
|
+
|
|
659
|
+
function ZoomableComponent() {
|
|
660
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
661
|
+
const [zoom, setZoom] = useState(1);
|
|
662
|
+
|
|
663
|
+
useWheelZoom(
|
|
664
|
+
ref,
|
|
665
|
+
(delta) => setZoom((z) => Math.max(0.5, Math.min(3, z + delta))),
|
|
666
|
+
{ enabled: true, sensitivity: 0.001 }
|
|
667
|
+
);
|
|
668
|
+
|
|
669
|
+
return <div ref={ref} style={{ transform: `scale(${zoom})` }}>Zoom me!</div>;
|
|
670
|
+
}
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
---
|
|
674
|
+
|
|
675
|
+
## Instance Methods
|
|
676
|
+
|
|
677
|
+
Access via `ref`:
|
|
678
|
+
|
|
679
|
+
```tsx
|
|
680
|
+
const flipbookRef = useRef<FlipbookInstance>(null);
|
|
681
|
+
|
|
682
|
+
// Navigation
|
|
683
|
+
flipbookRef.current?.nextPage();
|
|
684
|
+
flipbookRef.current?.prevPage();
|
|
685
|
+
flipbookRef.current?.firstPage();
|
|
686
|
+
flipbookRef.current?.lastPage();
|
|
687
|
+
flipbookRef.current?.goToPage(5);
|
|
688
|
+
|
|
689
|
+
// Zoom
|
|
690
|
+
flipbookRef.current?.zoomIn();
|
|
691
|
+
flipbookRef.current?.zoomOut();
|
|
692
|
+
flipbookRef.current?.zoomTo(1.5);
|
|
693
|
+
|
|
694
|
+
// State
|
|
695
|
+
flipbookRef.current?.getCurrentPage();
|
|
696
|
+
flipbookRef.current?.getNumPages();
|
|
697
|
+
flipbookRef.current?.getZoom();
|
|
698
|
+
flipbookRef.current?.isFullscreen();
|
|
699
|
+
|
|
700
|
+
// Fullscreen
|
|
701
|
+
flipbookRef.current?.toggleFullscreen();
|
|
702
|
+
|
|
703
|
+
// Bookmarks
|
|
704
|
+
flipbookRef.current?.bookmarkPage(5);
|
|
705
|
+
flipbookRef.current?.removeBookmark(5);
|
|
706
|
+
flipbookRef.current?.getBookmarkedPages();
|
|
707
|
+
|
|
708
|
+
// Cleanup
|
|
709
|
+
flipbookRef.current?.destroy();
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
---
|
|
713
|
+
|
|
714
|
+
## Styling & Theming
|
|
715
|
+
|
|
716
|
+
### Built-in Themes
|
|
717
|
+
|
|
718
|
+
```tsx
|
|
719
|
+
<Flipbook pages={pages} skin="dark" /> // Default dark theme
|
|
720
|
+
<Flipbook pages={pages} skin="light" /> // Light theme
|
|
721
|
+
<Flipbook pages={pages} skin="gradient" /> // Gradient theme
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
### CSS Custom Properties
|
|
725
|
+
|
|
726
|
+
```css
|
|
727
|
+
.react-flipbook {
|
|
728
|
+
--flipbook-primary: #4a9eff;
|
|
729
|
+
--flipbook-background: #515558;
|
|
730
|
+
--flipbook-text: #ffffff;
|
|
731
|
+
--flipbook-btn-bg: rgba(255, 255, 255, 0.1);
|
|
732
|
+
--flipbook-btn-hover: rgba(255, 255, 255, 0.2);
|
|
733
|
+
--flipbook-sidebar-bg: #2a2a2a;
|
|
734
|
+
--flipbook-sidebar-width: 280px;
|
|
735
|
+
}
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
### CSS Classes
|
|
739
|
+
|
|
740
|
+
| Class | Description |
|
|
741
|
+
|-------|-------------|
|
|
742
|
+
| `.react-flipbook` | Main container |
|
|
743
|
+
| `.react-flipbook-container` | Book container |
|
|
744
|
+
| `.react-flipbook-book` | Book element |
|
|
745
|
+
| `.react-flipbook-pages` | Pages wrapper |
|
|
746
|
+
| `.react-flipbook-page` | Individual page |
|
|
747
|
+
| `.react-flipbook-menu` | Bottom toolbar |
|
|
748
|
+
| `.react-flipbook-btn` | Toolbar buttons |
|
|
749
|
+
| `.react-flipbook-nav-btn` | Side navigation buttons |
|
|
750
|
+
| `.react-flipbook-sidebar` | Side panels |
|
|
751
|
+
| `.react-flipbook-thumbnails` | Thumbnails grid |
|
|
752
|
+
| `.react-flipbook-toc` | Table of contents |
|
|
753
|
+
| `.react-flipbook-search` | Search panel |
|
|
754
|
+
| `.react-flipbook-bookmarks` | Bookmarks panel |
|
|
755
|
+
| `.react-flipbook-theme-dark` | Dark theme |
|
|
756
|
+
| `.react-flipbook-theme-light` | Light theme |
|
|
757
|
+
| `.react-flipbook-theme-gradient` | Gradient theme |
|
|
758
|
+
|
|
759
|
+
---
|
|
760
|
+
|
|
761
|
+
## Accessibility
|
|
762
|
+
|
|
763
|
+
### Keyboard Shortcuts
|
|
764
|
+
|
|
765
|
+
| Key | Action |
|
|
766
|
+
|-----|--------|
|
|
767
|
+
| `←` / `ArrowLeft` | Previous page |
|
|
768
|
+
| `→` / `ArrowRight` | Next page |
|
|
769
|
+
| `Home` | First page |
|
|
770
|
+
| `End` | Last page |
|
|
771
|
+
| `+` / `=` | Zoom in |
|
|
772
|
+
| `-` | Zoom out |
|
|
773
|
+
| `0` | Reset zoom |
|
|
774
|
+
| `F` | Toggle fullscreen |
|
|
775
|
+
| `Escape` | Exit fullscreen |
|
|
776
|
+
|
|
777
|
+
### ARIA Support
|
|
778
|
+
|
|
779
|
+
- Full keyboard navigation
|
|
780
|
+
- ARIA labels and roles on all interactive elements
|
|
781
|
+
- Screen reader announcements for page changes
|
|
782
|
+
|
|
783
|
+
---
|
|
784
|
+
|
|
785
|
+
## Utility Functions
|
|
786
|
+
|
|
787
|
+
```tsx
|
|
788
|
+
import {
|
|
789
|
+
clamp,
|
|
790
|
+
lerp,
|
|
791
|
+
debounce,
|
|
792
|
+
throttle,
|
|
793
|
+
isMobile,
|
|
794
|
+
isTouchDevice,
|
|
795
|
+
isWebGLSupported,
|
|
796
|
+
preloadImage,
|
|
797
|
+
preloadImages,
|
|
798
|
+
generateId,
|
|
799
|
+
requestFullscreen,
|
|
800
|
+
exitFullscreen,
|
|
801
|
+
isFullscreen,
|
|
802
|
+
storage,
|
|
803
|
+
easing,
|
|
804
|
+
} from 'react-3d-flipbook';
|
|
805
|
+
|
|
806
|
+
// Clamp value between min and max
|
|
807
|
+
const clamped = clamp(value, 0, 100);
|
|
808
|
+
|
|
809
|
+
// Linear interpolation
|
|
810
|
+
const interpolated = lerp(start, end, 0.5);
|
|
811
|
+
|
|
812
|
+
// Debounce a function
|
|
813
|
+
const debouncedFn = debounce(fn, 300);
|
|
814
|
+
|
|
815
|
+
// Check device capabilities
|
|
816
|
+
if (isMobile()) { /* mobile device */ }
|
|
817
|
+
if (isTouchDevice()) { /* touch device */ }
|
|
818
|
+
if (isWebGLSupported()) { /* WebGL available */ }
|
|
819
|
+
|
|
820
|
+
// Preload images
|
|
821
|
+
await preloadImages(urls, (loaded, total) => {
|
|
822
|
+
console.log(`Loaded ${loaded}/${total}`);
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
// Local storage helpers
|
|
826
|
+
storage.set('key', value);
|
|
827
|
+
const value = storage.get('key', defaultValue);
|
|
828
|
+
storage.remove('key');
|
|
829
|
+
|
|
830
|
+
// Easing functions for animations
|
|
831
|
+
const easedValue = easing.easeInOutCubic(t);
|
|
832
|
+
const bounced = easing.easeOutQuart(t);
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
---
|
|
836
|
+
|
|
837
|
+
## Browser Support
|
|
838
|
+
|
|
839
|
+
| Browser | Version | WebGL Support |
|
|
840
|
+
|---------|---------|---------------|
|
|
841
|
+
| Chrome | 80+ | ✅ |
|
|
842
|
+
| Firefox | 75+ | ✅ |
|
|
843
|
+
| Safari | 13+ | ✅ |
|
|
844
|
+
| Edge | 80+ | ✅ |
|
|
845
|
+
| iOS Safari | 13+ | ✅ |
|
|
846
|
+
| Android Chrome | 80+ | ✅ |
|
|
847
|
+
|
|
848
|
+
**Note:** WebGL is required for the 3D page-flip animations. The component displays a fallback message if WebGL is not available.
|
|
849
|
+
|
|
850
|
+
---
|
|
851
|
+
|
|
852
|
+
## Performance Tips
|
|
853
|
+
|
|
854
|
+
### For Large PDFs
|
|
855
|
+
|
|
856
|
+
```tsx
|
|
857
|
+
// Use lazy loading for large documents
|
|
858
|
+
const pages = [];
|
|
859
|
+
for await (const page of pdfToFlipbookPagesLazy(pdfUrl, {
|
|
860
|
+
scale: 1.5, // Lower scale = less memory
|
|
861
|
+
format: 'jpeg', // JPEG is smaller than PNG
|
|
862
|
+
quality: 0.8, // Lower quality = smaller files
|
|
863
|
+
})) {
|
|
864
|
+
pages.push(page);
|
|
865
|
+
setPages([...pages]); // Progressive loading
|
|
866
|
+
}
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
### Reduce Memory Usage
|
|
870
|
+
|
|
871
|
+
- Use `format: 'jpeg'` instead of `'png'`
|
|
872
|
+
- Lower `scale` value (1-1.5 instead of 2)
|
|
873
|
+
- Lower `quality` value (0.7-0.8)
|
|
874
|
+
- Use lazy loading with `pdfToFlipbookPagesLazy`
|
|
875
|
+
|
|
876
|
+
### Improve Rendering Performance
|
|
877
|
+
|
|
878
|
+
- Disable shadows: `shadows={false}`
|
|
879
|
+
- Disable antialiasing: `antialias={false}`
|
|
880
|
+
- Reduce page segments: `pageSegmentsW={20}`
|
|
881
|
+
|
|
882
|
+
---
|
|
883
|
+
|
|
884
|
+
## TypeScript
|
|
885
|
+
|
|
886
|
+
Full TypeScript support with exported types:
|
|
887
|
+
|
|
888
|
+
```tsx
|
|
889
|
+
import type {
|
|
890
|
+
// Page types
|
|
891
|
+
FlipbookPage,
|
|
892
|
+
TocItem,
|
|
893
|
+
|
|
894
|
+
// Component props
|
|
895
|
+
FlipbookProps,
|
|
896
|
+
FlipbookOptions,
|
|
897
|
+
WebGLPageFlipProps,
|
|
898
|
+
|
|
899
|
+
// Instance types
|
|
900
|
+
FlipbookInstance,
|
|
901
|
+
WebGLPageFlipInstance,
|
|
902
|
+
|
|
903
|
+
// Event types
|
|
904
|
+
FlipbookEventMap,
|
|
905
|
+
|
|
906
|
+
// Configuration types
|
|
907
|
+
SkinType,
|
|
908
|
+
ButtonConfig,
|
|
909
|
+
DownloadButtonConfig,
|
|
910
|
+
CurrentPageConfig,
|
|
911
|
+
|
|
912
|
+
// Hook types
|
|
913
|
+
UseAutoplayOptions,
|
|
914
|
+
UseAutoplayReturn,
|
|
915
|
+
UsePdfLoaderOptions,
|
|
916
|
+
UsePdfLoaderReturn,
|
|
917
|
+
|
|
918
|
+
// PDF utilities
|
|
919
|
+
PdfToImagesOptions,
|
|
920
|
+
PdfPageInfo,
|
|
921
|
+
FlipbookSizeOptions,
|
|
922
|
+
FlipbookSize,
|
|
923
|
+
|
|
924
|
+
// Panel types
|
|
925
|
+
SearchResult,
|
|
926
|
+
Bookmark,
|
|
927
|
+
} from 'react-3d-flipbook';
|
|
928
|
+
```
|
|
929
|
+
|
|
930
|
+
---
|
|
931
|
+
|
|
932
|
+
## Troubleshooting
|
|
933
|
+
|
|
934
|
+
### WebGL Issues
|
|
935
|
+
|
|
936
|
+
**Problem:** WebGL not working or blank screen
|
|
937
|
+
**Solution:** Ensure `three` is installed and browser supports WebGL. Check browser console for errors.
|
|
938
|
+
|
|
939
|
+
### PDF Issues
|
|
940
|
+
|
|
941
|
+
**Problem:** PDF not loading
|
|
942
|
+
**Solution:**
|
|
943
|
+
1. Ensure `pdfjs-dist` is installed
|
|
944
|
+
2. Call `setPdfWorkerSrc()` before loading PDFs
|
|
945
|
+
3. Check CORS settings if loading from external URLs
|
|
946
|
+
|
|
947
|
+
**Problem:** PDF pages are blurry
|
|
948
|
+
**Solution:** Increase `scale` option (e.g., `scale: 2` or `scale: 3`)
|
|
949
|
+
|
|
950
|
+
### Performance Issues
|
|
951
|
+
|
|
952
|
+
**Problem:** Slow loading or high memory usage
|
|
953
|
+
**Solution:**
|
|
954
|
+
1. Use `pdfToFlipbookPagesLazy` for progressive loading
|
|
955
|
+
2. Lower `scale` and `quality` options
|
|
956
|
+
3. Use JPEG format instead of PNG
|
|
957
|
+
4. Disable shadows and reduce page segments
|
|
958
|
+
|
|
959
|
+
### Animation Issues
|
|
960
|
+
|
|
961
|
+
**Problem:** Page flip animation is jerky
|
|
962
|
+
**Solution:**
|
|
963
|
+
1. Reduce `pageSegmentsW` value
|
|
964
|
+
2. Disable antialiasing if not needed
|
|
965
|
+
3. Ensure consistent page aspect ratios
|
|
966
|
+
|
|
967
|
+
**Problem:** Pages don't align in two-page mode
|
|
968
|
+
**Solution:** Ensure all pages have consistent dimensions or let the library auto-size based on first page.
|
|
969
|
+
|
|
970
|
+
---
|
|
971
|
+
|
|
972
|
+
## Contributing
|
|
973
|
+
|
|
974
|
+
Contributions are welcome! Please fork, branch, and submit a PR.
|
|
975
|
+
|
|
976
|
+
1. Fork the repository
|
|
977
|
+
2. Create a feature branch: `git checkout -b feature/my-feature`
|
|
978
|
+
3. Commit changes: `git commit -am 'Add new feature'`
|
|
979
|
+
4. Push to branch: `git push origin feature/my-feature`
|
|
980
|
+
5. Submit a Pull Request
|
|
981
|
+
|
|
982
|
+
---
|
|
983
|
+
|
|
984
|
+
## License
|
|
985
|
+
|
|
986
|
+
MIT License
|
|
987
|
+
|
|
988
|
+
---
|
|
989
|
+
|
|
990
|
+
## Changelog
|
|
991
|
+
|
|
992
|
+
### v1.1.0
|
|
993
|
+
- Added automatic page sizing based on PDF dimensions
|
|
994
|
+
- Added `calculateFlipbookSize` and `calculateFlipbookSizeFromPages` utilities
|
|
995
|
+
- Improved camera positioning for better framing of content
|
|
996
|
+
- Fixed two-page spread alignment issues
|
|
997
|
+
- Improved support for landscape PDFs
|
|
998
|
+
|
|
999
|
+
### v1.0.0
|
|
1000
|
+
- Initial release
|
|
1001
|
+
- WebGL 3D page flip animations
|
|
1002
|
+
- PDF support via PDF.js
|
|
1003
|
+
- Single-page and two-page spread modes
|
|
1004
|
+
- Thumbnails, TOC, Search, and Bookmarks panels
|
|
1005
|
+
- Multiple themes and full customization
|