leafthrough 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 +3 -1
- package/dist/LeafThrough.d.ts +2 -0
- package/dist/{chunk-U2OBPSZN.js → chunk-DQL4R32K.js} +60 -11
- package/dist/chunk-DQL4R32K.js.map +1 -0
- package/dist/element.js +1 -1
- package/dist/engine.d.ts +8 -0
- package/dist/index.js +1 -1
- package/dist/options.d.ts +4 -3
- package/dist/plain/PlainEngine.d.ts +4 -0
- package/dist/react.js +1 -1
- package/dist/single/SingleEngine.d.ts +6 -0
- package/dist/spread/SpreadEngine.d.ts +4 -0
- package/dist/svelte.js +1 -1
- package/dist/textures.d.ts +6 -2
- package/dist/vue.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-U2OBPSZN.js.map +0 -1
package/README.md
CHANGED
|
@@ -166,7 +166,7 @@ The magazine fills the element you give it and keeps its proportions, centred.
|
|
|
166
166
|
| `motion` | `MotionOptions` | | See below. |
|
|
167
167
|
| `interaction` | `InteractionOptions` | | See below. |
|
|
168
168
|
| `maxPixelRatio` | `number` | `2` | The canvas is also capped at about 15 megapixels. |
|
|
169
|
-
| `preload` | `number` | `6` | Pages either side of the visible ones to load ahead. Pages twice as far away unload. `Infinity` keeps all. |
|
|
169
|
+
| `preload` | `number` | `6` | Pages either side of the visible ones (mid-turn and rolled-up ones included) to load ahead. Pages twice as far away unload. `Infinity` keeps all. |
|
|
170
170
|
| `plain` | `boolean` | `false` | Flat pages without 3D. Used by itself when the browser can't run WebGL. |
|
|
171
171
|
| `label` | `string` | `'Magazine'` | Accessible name. |
|
|
172
172
|
| `formatPageLabel` | `(detail) => string` | `"Pages 14–15 of 28"` | What screen readers hear when pages change. |
|
|
@@ -274,6 +274,8 @@ pages: [
|
|
|
274
274
|
the reader as they turn or jump. Pages that haven't arrived show blank paper.
|
|
275
275
|
- **Memory.** Pages within `preload` of the visible ones load; pages more than twice that
|
|
276
276
|
far away unload, freeing their GPU memory, and load again when the reader comes back.
|
|
277
|
+
Visible means anything on screen: pages still turning in a quick riffle, and in `single`
|
|
278
|
+
the read pages rolled up behind the magazine, until they settle under the pile.
|
|
277
279
|
A texture you pass in yourself stays loaded, since it's yours.
|
|
278
280
|
- **Failures.** A page that fails stays blank paper and is reported through `onError`.
|
|
279
281
|
`retry()` loads failed pages again, and so does the browser coming back online.
|
package/dist/LeafThrough.d.ts
CHANGED
|
@@ -580,6 +580,9 @@ var PlainEngine = class {
|
|
|
580
580
|
get turning() {
|
|
581
581
|
return false;
|
|
582
582
|
}
|
|
583
|
+
pagesInView() {
|
|
584
|
+
return this.spread ? { from: Math.max(2 * this.current - 1, 0), to: 2 * this.current } : { from: this.current, to: this.current };
|
|
585
|
+
}
|
|
583
586
|
// closed, only the cover side can be clicked
|
|
584
587
|
get activeSide() {
|
|
585
588
|
if (!this.spread) return "both";
|
|
@@ -893,6 +896,8 @@ var SingleEngine = class {
|
|
|
893
896
|
fallDuration;
|
|
894
897
|
abort = new AbortController();
|
|
895
898
|
current;
|
|
899
|
+
turned = 0;
|
|
900
|
+
// sum of every sheet's progress, as of the last update
|
|
896
901
|
flights = [];
|
|
897
902
|
drag = null;
|
|
898
903
|
// the pointer that started the gesture; other pointers are ignored until it lifts
|
|
@@ -964,6 +969,22 @@ var SingleEngine = class {
|
|
|
964
969
|
return this.flights.length > 0 || this.drag !== null;
|
|
965
970
|
}
|
|
966
971
|
activeSide = "both";
|
|
972
|
+
pagesInView() {
|
|
973
|
+
const settled = Math.floor(this.paced(this.turned) - 1 - this.fallDelay - this.fallDuration);
|
|
974
|
+
let from = clamp(settled + 1, 0, Math.max(this.current - 1, 0));
|
|
975
|
+
let to = Math.max(Math.min(this.current, this.count - 1), this.pileTop()?.index ?? 0);
|
|
976
|
+
const turning = this.flights.map((flight) => flight.sheet);
|
|
977
|
+
if (this.drag) turning.push(this.drag.sheet);
|
|
978
|
+
for (const sheet of turning) {
|
|
979
|
+
from = Math.min(from, sheet.index);
|
|
980
|
+
to = Math.max(to, sheet.index);
|
|
981
|
+
}
|
|
982
|
+
return { from, to: Math.max(from, to) };
|
|
983
|
+
}
|
|
984
|
+
// the page on top of the pile, the one a turning page reveals
|
|
985
|
+
pileTop() {
|
|
986
|
+
return this.sheets.find((sheet) => sheet.index >= this.current && !this.busy(sheet));
|
|
987
|
+
}
|
|
967
988
|
setPageTexture(page, texture) {
|
|
968
989
|
const sheet = this.sheets[page];
|
|
969
990
|
if (!sheet) return;
|
|
@@ -1313,16 +1334,18 @@ var SingleEngine = class {
|
|
|
1313
1334
|
if (!moving && !this.dirty) return false;
|
|
1314
1335
|
let turned = 0;
|
|
1315
1336
|
for (const sheet of this.sheets) turned += sheet.progress;
|
|
1337
|
+
this.turned = turned;
|
|
1316
1338
|
const edge = this.pileEdge(turned);
|
|
1317
|
-
const top = this.
|
|
1339
|
+
const top = this.pileTop();
|
|
1318
1340
|
for (const sheet of this.sheets) {
|
|
1319
1341
|
this.applyUniforms(sheet, turned, edge);
|
|
1320
1342
|
sheet.uniforms.uTrim.value.set(sheet === top || this.busy(sheet) ? 0 : 1, edge.bottom, edge.top);
|
|
1321
1343
|
}
|
|
1322
1344
|
const closing = this.closing(turned) > 0;
|
|
1345
|
+
const pileFrom = Math.max(this.current, top?.index ?? this.current);
|
|
1323
1346
|
for (const sheet of this.sheets) {
|
|
1324
1347
|
const i = sheet.index;
|
|
1325
|
-
sheet.mesh.visible = closing || i <=
|
|
1348
|
+
sheet.mesh.visible = closing || i <= pileFrom + PILE.renderDepth || this.busy(sheet);
|
|
1326
1349
|
layerSheet(sheet.mesh, i < this.current ? this.current - 1 - i : i - this.current, this.count, this.busy(sheet));
|
|
1327
1350
|
}
|
|
1328
1351
|
const read = smooth(this.count > 1 ? this.paced(turned) / (this.count - 1) : 0) * (1 - this.shutting(turned));
|
|
@@ -1671,6 +1694,16 @@ var SpreadEngine = class {
|
|
|
1671
1694
|
get turning() {
|
|
1672
1695
|
return this.flights.length > 0 || this.drag !== null;
|
|
1673
1696
|
}
|
|
1697
|
+
pagesInView() {
|
|
1698
|
+
let from = Math.max(2 * this.current - 1, 0);
|
|
1699
|
+
let to = 2 * this.current;
|
|
1700
|
+
for (const sheet of this.sheets) {
|
|
1701
|
+
if (sheet.progress <= 0 || sheet.progress >= 1) continue;
|
|
1702
|
+
from = Math.min(from, 2 * sheet.index);
|
|
1703
|
+
to = Math.max(to, 2 * sheet.index + 1);
|
|
1704
|
+
}
|
|
1705
|
+
return { from, to };
|
|
1706
|
+
}
|
|
1674
1707
|
// closed, only the cover side can be grabbed
|
|
1675
1708
|
get activeSide() {
|
|
1676
1709
|
const busy = this.flights.length > 0 || this.drag !== null || this.hold !== null;
|
|
@@ -2099,9 +2132,10 @@ var PageLoader = class {
|
|
|
2099
2132
|
failed = /* @__PURE__ */ new Set();
|
|
2100
2133
|
// `first` pages load before the rest; the rest go nearest first
|
|
2101
2134
|
first = [];
|
|
2102
|
-
// the
|
|
2135
|
+
// the pages in view, and the one being read, which loads first among them
|
|
2103
2136
|
from = 0;
|
|
2104
2137
|
to = 0;
|
|
2138
|
+
around = 0;
|
|
2105
2139
|
// whether the magazine loops, putting the cover right after the last page
|
|
2106
2140
|
wrap = false;
|
|
2107
2141
|
waiting = /* @__PURE__ */ new Set();
|
|
@@ -2122,11 +2156,15 @@ var PageLoader = class {
|
|
|
2122
2156
|
if (!this.waiting.size) this.settleFirst();
|
|
2123
2157
|
return ready;
|
|
2124
2158
|
}
|
|
2125
|
-
/**
|
|
2126
|
-
|
|
2159
|
+
/**
|
|
2160
|
+
* Follows the reader: loads around the pages in view (`from`–`to`), the page being read
|
|
2161
|
+
* (`around`) first, and unloads pages left far behind.
|
|
2162
|
+
*/
|
|
2163
|
+
focus(from, to = from, around = from) {
|
|
2127
2164
|
if (this.disposed) return;
|
|
2128
2165
|
this.from = from;
|
|
2129
2166
|
this.to = to;
|
|
2167
|
+
this.around = around;
|
|
2130
2168
|
const keep = 2 * this.radius;
|
|
2131
2169
|
for (const [i, request] of this.inFlight) {
|
|
2132
2170
|
if (this.distance(i) <= keep) continue;
|
|
@@ -2145,11 +2183,11 @@ var PageLoader = class {
|
|
|
2145
2183
|
setWrap(wrap) {
|
|
2146
2184
|
if (wrap === this.wrap) return;
|
|
2147
2185
|
this.wrap = wrap;
|
|
2148
|
-
this.focus(this.from, this.to);
|
|
2186
|
+
this.focus(this.from, this.to, this.around);
|
|
2149
2187
|
}
|
|
2150
2188
|
setRadius(radius) {
|
|
2151
2189
|
this.radius = radius;
|
|
2152
|
-
this.focus(this.from, this.to);
|
|
2190
|
+
this.focus(this.from, this.to, this.around);
|
|
2153
2191
|
}
|
|
2154
2192
|
/** Loads the pages that failed again. */
|
|
2155
2193
|
retry() {
|
|
@@ -2175,7 +2213,7 @@ var PageLoader = class {
|
|
|
2175
2213
|
const wanted = (i) => !this.textures[i] && !this.inFlight.has(i) && !this.failed.has(i) && this.distance(i) <= this.radius;
|
|
2176
2214
|
const queue = [
|
|
2177
2215
|
...this.first.filter((i) => this.waiting.has(i)),
|
|
2178
|
-
...this.sources.map((_, i) => i).sort((a, b) => this.distance(a) - this.distance(b))
|
|
2216
|
+
...this.sources.map((_, i) => i).sort((a, b) => this.distance(a) - this.distance(b) || Math.abs(a - this.around) - Math.abs(b - this.around))
|
|
2179
2217
|
];
|
|
2180
2218
|
for (const page of queue) {
|
|
2181
2219
|
if (this.inFlight.size >= CONCURRENCY) return;
|
|
@@ -2707,6 +2745,7 @@ var LeafThrough = class extends EventTarget {
|
|
|
2707
2745
|
onError: (page, error) => this.fail(error, page)
|
|
2708
2746
|
});
|
|
2709
2747
|
this.loader = loader;
|
|
2748
|
+
this.inView = "";
|
|
2710
2749
|
this.resetReady();
|
|
2711
2750
|
const first = [around - 1, around, around + 1, around + 2];
|
|
2712
2751
|
const visible = this.pagesAt(this.pageToPosition(around, layout), layout);
|
|
@@ -2716,6 +2755,17 @@ var LeafThrough = class extends EventTarget {
|
|
|
2716
2755
|
this.needsRender = true;
|
|
2717
2756
|
});
|
|
2718
2757
|
}
|
|
2758
|
+
// loads around what's on screen, pages mid-turn included, the page being read first
|
|
2759
|
+
inView = "";
|
|
2760
|
+
followReader(engine, layout) {
|
|
2761
|
+
if (!this.loader) return;
|
|
2762
|
+
const { from, to } = engine.pagesInView();
|
|
2763
|
+
const around = this.pagesAt(engine.position, layout)[0] ?? to;
|
|
2764
|
+
const key = `${from}:${to}:${around}`;
|
|
2765
|
+
if (key === this.inView) return;
|
|
2766
|
+
this.inView = key;
|
|
2767
|
+
this.loader.focus(from, to, around);
|
|
2768
|
+
}
|
|
2719
2769
|
resetReady() {
|
|
2720
2770
|
this.ready = false;
|
|
2721
2771
|
this.element.dataset.state = "loading";
|
|
@@ -2759,8 +2809,6 @@ var LeafThrough = class extends EventTarget {
|
|
|
2759
2809
|
const first = !this.lastDetailKey;
|
|
2760
2810
|
this.lastDetailKey = key;
|
|
2761
2811
|
this.live.textContent = (this.options.formatPageLabel ?? defaultPageLabel)(detail);
|
|
2762
|
-
const { visiblePages: visible } = detail;
|
|
2763
|
-
this.loader?.focus(visible[0] ?? detail.page, visible[visible.length - 1] ?? detail.page);
|
|
2764
2812
|
if (first) return;
|
|
2765
2813
|
this.options.onPageChange?.(detail);
|
|
2766
2814
|
this.dispatchEvent(new CustomEvent("pagechange", { detail }));
|
|
@@ -2867,6 +2915,7 @@ var LeafThrough = class extends EventTarget {
|
|
|
2867
2915
|
if (!view) return;
|
|
2868
2916
|
this.stepRiffle(now, view.engine);
|
|
2869
2917
|
const moved = view.engine.update(now);
|
|
2918
|
+
this.followReader(view.engine, view.settings.layout);
|
|
2870
2919
|
if (view.engine.activeSide !== this.hitSide) this.placeHitArea();
|
|
2871
2920
|
if (moved || this.needsRender) {
|
|
2872
2921
|
if (this.renderer && view.scene && view.camera) {
|
|
@@ -2926,4 +2975,4 @@ export {
|
|
|
2926
2975
|
defaultPageLabel,
|
|
2927
2976
|
LeafThrough
|
|
2928
2977
|
};
|
|
2929
|
-
//# sourceMappingURL=chunk-
|
|
2978
|
+
//# sourceMappingURL=chunk-DQL4R32K.js.map
|