html-flip-book-vanilla 0.0.0-alpha.21 → 0.0.0-alpha.23
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/dist/flipbook.d.ts.map +1 -1
- package/dist/flipbook.js +71 -67
- package/dist/flipbook.js.map +1 -1
- package/package.json +1 -1
package/dist/flipbook.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flipbook.d.ts","sourceRoot":"","sources":["../src/flipbook.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,CAAC;AACtB,OAAO,iBAAiB,CAAC;AAIzB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAG3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAyCtD,cAAM,QAAQ;IACb,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAwC;IACxE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAG/B;IACF,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwB;IAClD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA8B;IACjE,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA0B;IAC9D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAA8B;IAC7D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA4B;IAC1D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAS;IACvC,OAAO,CAAC,MAAM,CAAc;IAE5B,OAAO,CAAC,WAAW,CAAqC;IACxD,OAAO,CAAC,sBAAsB,CAAK;IACnC,OAAO,CAAC,oBAAoB,CAAqC;IACjE,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,WAAW,CAAmB;IACtC,gBAAgB;;;MAAkB;IAElC,OAAO,CAAC,wBAAwB,CAAS;IACzC,OAAO,CAAC,sBAAsB,CAA0C;IAExE,OAAO,CAAC,MAAM,CAA4B;IAE1C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAA6B;IAErE,OAAO,CAAC,kBAAkB;IAG1B,OAAO,KAAK,KAAK,GAEhB;IAED,OAAO,KAAK,sBAAsB,GAcjC;gBAEW,OAAO,EAAE,eAAe;IAYpC,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,UAAQ;IAgKtC,OAAO,CAAC,4BAA4B;IA8BpC,OAAO,CAAC,YAAY;IAqBpB,OAAO,KAAK,iBAAiB,GAO5B;IAMD,OAAO,CAAC,oBAAoB;IAgC5B,OAAO,CAAC,WAAW;IAkBnB,OAAO,CAAC,YAAY;IA0EpB,OAAO,CAAC,SAAS;IAkDjB,OAAO,CAAC,gBAAgB,CAOtB;IAEF,OAAO,CAAC,eAAe,CAerB;IAEF,OAAO,CAAC,eAAe,CA2CpB;IAEH,OAAO,CAAC,gBAAgB,CAEtB;IAEF,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,QAAQ;IA4BhB,IAAI,gBAAgB,IAAI,MAAM,CAE7B;IAKD,IAAI,UAAU,IAAI,MAAM,CAEvB;IAKD,IAAI,WAAW,IAAI,OAAO,CAEzB;IAKD,IAAI,UAAU,IAAI,OAAO,CAGxB;IAKD,OAAO,CAAC,4BAA4B;IAa9B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IA6BzB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IA+BzB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgChD,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"flipbook.d.ts","sourceRoot":"","sources":["../src/flipbook.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,CAAC;AACtB,OAAO,iBAAiB,CAAC;AAIzB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAG3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAyCtD,cAAM,QAAQ;IACb,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAwC;IACxE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAG/B;IACF,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwB;IAClD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA8B;IACjE,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA0B;IAC9D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAA8B;IAC7D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA4B;IAC1D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAS;IACvC,OAAO,CAAC,MAAM,CAAc;IAE5B,OAAO,CAAC,WAAW,CAAqC;IACxD,OAAO,CAAC,sBAAsB,CAAK;IACnC,OAAO,CAAC,oBAAoB,CAAqC;IACjE,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,WAAW,CAAmB;IACtC,gBAAgB;;;MAAkB;IAElC,OAAO,CAAC,wBAAwB,CAAS;IACzC,OAAO,CAAC,sBAAsB,CAA0C;IAExE,OAAO,CAAC,MAAM,CAA4B;IAE1C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAA6B;IAErE,OAAO,CAAC,kBAAkB;IAG1B,OAAO,KAAK,KAAK,GAEhB;IAED,OAAO,KAAK,sBAAsB,GAcjC;gBAEW,OAAO,EAAE,eAAe;IAYpC,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,UAAQ;IAgKtC,OAAO,CAAC,4BAA4B;IA8BpC,OAAO,CAAC,YAAY;IAqBpB,OAAO,KAAK,iBAAiB,GAO5B;IAMD,OAAO,CAAC,oBAAoB;IAgC5B,OAAO,CAAC,WAAW;IAkBnB,OAAO,CAAC,YAAY;IA0EpB,OAAO,CAAC,SAAS;IAkDjB,OAAO,CAAC,gBAAgB,CAOtB;IAEF,OAAO,CAAC,eAAe,CAerB;IAEF,OAAO,CAAC,eAAe,CA2CpB;IAEH,OAAO,CAAC,gBAAgB,CAEtB;IAEF,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,QAAQ;IA4BhB,IAAI,gBAAgB,IAAI,MAAM,CAE7B;IAKD,IAAI,UAAU,IAAI,MAAM,CAEvB;IAKD,IAAI,WAAW,IAAI,OAAO,CAEzB;IAKD,IAAI,UAAU,IAAI,OAAO,CAGxB;IAKD,OAAO,CAAC,4BAA4B;IAa9B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IA6BzB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IA+BzB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgChD,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAmFnC,OAAO,CAAC,kBAAkB;IAQ1B,OAAO;CAeP;AAED,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,CAAC"}
|
package/dist/flipbook.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import A from "hammerjs";
|
|
2
2
|
import { throttle as b } from "throttle-debounce";
|
|
3
|
-
var
|
|
3
|
+
var c = /* @__PURE__ */ ((v) => (v.Forward = "Forward", v.Backward = "Backward", v.None = "None", v))(c || {});
|
|
4
4
|
const R = 1.1, E = 0.9, k = 8;
|
|
5
5
|
class D {
|
|
6
|
-
constructor(t, e, i, s,
|
|
7
|
-
this.index = t, this.pages = e, this.bookProperties = s, this.onTurned =
|
|
6
|
+
constructor(t, e, i, s, r) {
|
|
7
|
+
this.index = t, this.pages = e, this.bookProperties = s, this.onTurned = r, this.wrappedFlipPosition = i ? 1 : 0;
|
|
8
8
|
}
|
|
9
9
|
currentAnimation = null;
|
|
10
10
|
targetFlipPosition = null;
|
|
@@ -37,28 +37,28 @@ class D {
|
|
|
37
37
|
}
|
|
38
38
|
async flipToPosition(t, e = 225) {
|
|
39
39
|
return this.currentAnimation && await this.currentAnimation, this.flipPosition === t ? Promise.resolve() : this.targetFlipPosition === t ? this.currentAnimation ?? Promise.resolve() : (this.targetFlipPosition = t, this.currentAnimation = new Promise((i) => {
|
|
40
|
-
const s = this.flipPosition, o = Math.abs(t - s) * 180 / e * 1e3, n = performance.now(),
|
|
40
|
+
const s = this.flipPosition, o = Math.abs(t - s) * 180 / e * 1e3, n = performance.now(), l = (f) => {
|
|
41
41
|
const d = f - n;
|
|
42
42
|
if (d < 0) {
|
|
43
|
-
requestAnimationFrame(
|
|
43
|
+
requestAnimationFrame(l);
|
|
44
44
|
return;
|
|
45
45
|
}
|
|
46
|
-
const
|
|
47
|
-
this.applyTransform(
|
|
46
|
+
const a = Math.min(d / o, 1), h = s + a * (t - s);
|
|
47
|
+
this.applyTransform(h), this.flipPosition = Math.max(0, Math.min(1, h)), (this.flipPosition === 1 || this.flipPosition === 0) && this.onTurned(this.flipPosition === 1 ? c.Forward : c.Backward), a < 1 ? requestAnimationFrame(l) : (this.currentAnimation = null, this.targetFlipPosition = null, i());
|
|
48
48
|
};
|
|
49
|
-
requestAnimationFrame(
|
|
49
|
+
requestAnimationFrame(l);
|
|
50
50
|
}), this.currentAnimation);
|
|
51
51
|
}
|
|
52
52
|
async efficientFlipToPosition(t, e = 2e4) {
|
|
53
53
|
return b(1, this.flipToPosition.bind(this))(t, e);
|
|
54
54
|
}
|
|
55
55
|
applyTransform(t) {
|
|
56
|
-
const e = Math.max(0, Math.min(1, t)), i = Math.sin(e * Math.PI), s = Math.max(i, this.hoverShadow),
|
|
57
|
-
this.pages.forEach((
|
|
56
|
+
const e = Math.max(0, Math.min(1, t)), i = Math.sin(e * Math.PI), s = Math.max(i, this.hoverShadow), r = Math.min(1, s * R), o = Math.min(1, s * E), n = `${(s * k).toFixed(3)}px`;
|
|
57
|
+
this.pages.forEach((l, f) => {
|
|
58
58
|
const d = this.bookProperties.isLTR;
|
|
59
|
-
if (!
|
|
60
|
-
const
|
|
61
|
-
|
|
59
|
+
if (!l) return;
|
|
60
|
+
const a = f % 2 + 1 === 1, u = `${a ? d ? e > 0.5 ? 180 - e * 180 : -e * 180 : e > 0.5 ? -(180 - e * 180) : e * 180 : d ? e < 0.5 ? -e * 180 : 180 - e * 180 : e < 0.5 ? e * 180 : -(180 - e * 180)}deg`, L = `${a ? d ? "100%" : "-100%" : "0px"}`, g = a ? e > 0.5 ? -1 : 1 : e < 0.5 ? -1 : 1, m = a ? d ? "left" : "right" : d ? "right" : "left", w = m === "left" ? "right" : "left";
|
|
61
|
+
l.style.transform = `translateX(${L})rotateY(${u})scaleX(${g})`, l.style.transformOrigin = m, l.style.zIndex = `${e > 0.5 ? l.dataset.pageIndex : this.bookProperties.pagesCount - l.dataset.pageIndex}`, l.style.setProperty("--inner-shadow-shadow", r.toFixed(3)), l.style.setProperty("--inner-shadow-highlight", o.toFixed(3)), l.style.setProperty("--inner-shadow-lift", n), l.style.setProperty("--inner-shadow-edge", w);
|
|
62
62
|
});
|
|
63
63
|
}
|
|
64
64
|
}
|
|
@@ -106,7 +106,7 @@ class S {
|
|
|
106
106
|
// flipping state - supports concurrent page flipping
|
|
107
107
|
activeFlips = /* @__PURE__ */ new Map();
|
|
108
108
|
pendingFlipStartingPos = 0;
|
|
109
|
-
pendingFlipDirection =
|
|
109
|
+
pendingFlipDirection = c.None;
|
|
110
110
|
isDragging = !1;
|
|
111
111
|
hoveredLeaf;
|
|
112
112
|
touchStartingPos = { x: 0, y: 0 };
|
|
@@ -145,33 +145,33 @@ class S {
|
|
|
145
145
|
if (!s.length)
|
|
146
146
|
throw new Error("No pages found in flipbook");
|
|
147
147
|
this.pageElements = Array.from(s), this.leaves.splice(0, this.leaves.length);
|
|
148
|
-
const
|
|
148
|
+
const r = Math.ceil(this.pagesCount / 2), n = new P(this.bookElement.clientWidth / 2, this.bookElement.clientHeight).aspectRatioFit(this.coverAspectRatio), l = new P(
|
|
149
149
|
n.width * this.leafAspectRatio.width / this.coverAspectRatio.width,
|
|
150
150
|
n.height * this.leafAspectRatio.height / this.coverAspectRatio.height
|
|
151
151
|
);
|
|
152
|
-
this.bookElement.style.perspective = `${Math.min(
|
|
153
|
-
|
|
154
|
-
const u = Math.floor(
|
|
155
|
-
if (
|
|
152
|
+
this.bookElement.style.perspective = `${Math.min(l.width * 2, l.height) * 2}px`, this.pageElements.forEach((a, h) => {
|
|
153
|
+
a.style.width = `${l.width}px`, a.style.height = `${l.height}px`, a.style.zIndex = `${this.pagesCount - h}`, a.dataset.pageIndex = h.toString(), a.style[this.isLTR ? "left" : "right"] = `${(i.clientWidth - 2 * l.width) / 2}px`, a.style.top = `${(i.clientHeight - l.height) / 2}px`, a.dataset.pageSemanticName = this.pageSemantics?.indexToSemanticName(h) ?? "", a.dataset.pageTitle = this.pageSemantics?.indexToTitle(h) ?? "";
|
|
154
|
+
const u = Math.floor(h / 2), L = (h + 1) % 2 === 1, g = this.initialTurnedLeaves.has(u), m = this.initialTurnedLeaves.size > 0 ? Math.max(...this.initialTurnedLeaves) + 1 : 0, w = u === m || u === m - 1 && g;
|
|
155
|
+
if (a.classList.add(
|
|
156
156
|
L ? "odd" : "even",
|
|
157
157
|
...w ? ["current-page"] : []
|
|
158
158
|
), L) {
|
|
159
159
|
if (g) {
|
|
160
160
|
const p = `translateX(${this.isLTR ? "100%" : "-100%"})rotateY(${this.isLTR ? "180deg" : "-180deg"})scaleX(-1)`;
|
|
161
|
-
|
|
161
|
+
a.style.transform = p, a.style.transformOrigin = this.isLTR ? "left" : "right", a.style.zIndex = `${h}`;
|
|
162
162
|
} else
|
|
163
|
-
|
|
163
|
+
a.style.transform = `translateX(${this.isLTR ? "" : "-"}100%)`, a.style.zIndex = `${this.pagesCount - h}`;
|
|
164
164
|
this.leaves[u] = new D(
|
|
165
165
|
u,
|
|
166
|
-
[
|
|
166
|
+
[a, void 0],
|
|
167
167
|
g,
|
|
168
168
|
{
|
|
169
169
|
isLTR: this.isLTR,
|
|
170
|
-
leavesCount:
|
|
170
|
+
leavesCount: r,
|
|
171
171
|
pagesCount: this.pagesCount
|
|
172
172
|
},
|
|
173
173
|
(F) => {
|
|
174
|
-
const p = F ===
|
|
174
|
+
const p = F === c.Forward ? h + 2 === this.pagesCount ? [h + 1] : [h + 1, h + 2] : h === 0 ? [h] : [h - 1, h];
|
|
175
175
|
if (this.prevVisiblePageIndices && this.prevVisiblePageIndices.length === p.length && p.every((x, M) => x === this.prevVisiblePageIndices?.[M]))
|
|
176
176
|
return;
|
|
177
177
|
const y = this.prevVisiblePageIndices;
|
|
@@ -179,12 +179,12 @@ class S {
|
|
|
179
179
|
}
|
|
180
180
|
);
|
|
181
181
|
} else
|
|
182
|
-
g ? (
|
|
182
|
+
g ? (a.style.transform = `translateX(0px)rotateY(${this.isLTR ? "180deg" : "-180deg"})scaleX(1)`, a.style.transformOrigin = this.isLTR ? "right" : "left", a.style.zIndex = `${h}`) : (a.style.transform = `scaleX(-1)translateX(${this.isLTR ? "-" : ""}100%)`, a.style.zIndex = `${this.pagesCount - h}`), this.leaves[u].pages[1] = a;
|
|
183
183
|
});
|
|
184
184
|
const d = (this.initialTurnedLeaves.size > 0 ? Math.max(...this.initialTurnedLeaves) + 1 : 0) * 2;
|
|
185
185
|
if (d >= this.pagesCount) {
|
|
186
|
-
const
|
|
187
|
-
this.prevVisiblePageIndices =
|
|
186
|
+
const h = (Math.ceil(this.pagesCount / 2) - 1) * 2;
|
|
187
|
+
this.prevVisiblePageIndices = h + 1 < this.pagesCount ? [h, h + 1] : [h];
|
|
188
188
|
} else
|
|
189
189
|
this.prevVisiblePageIndices = d + 1 < this.pagesCount ? [d, d + 1] : [d];
|
|
190
190
|
this.hammer = new A(this.bookElement), this.hammer.on("panstart", this.onDragStart.bind(this)), this.hammer.on("panmove", this.onDragUpdate.bind(this)), this.hammer.on("panend", this.onDragEnd.bind(this)), this.bookElement.addEventListener("touchstart", this.handleTouchStart.bind(this), {
|
|
@@ -205,10 +205,10 @@ class S {
|
|
|
205
205
|
return;
|
|
206
206
|
const t = this.leaves.length;
|
|
207
207
|
if (t === 0) return;
|
|
208
|
-
const [e, i] = this.currentOrTurningLeaves, s = i?.index ?? e?.index ?? 0,
|
|
208
|
+
const [e, i] = this.currentOrTurningLeaves, s = i?.index ?? e?.index ?? 0, r = Math.max(0, s - this.leavesBuffer), o = Math.min(t - 1, s + this.leavesBuffer);
|
|
209
209
|
for (let n = 0; n < t; n++) {
|
|
210
|
-
const
|
|
211
|
-
for (const d of
|
|
210
|
+
const l = this.leaves[n], f = n >= r && n <= o;
|
|
211
|
+
for (const d of l.pages)
|
|
212
212
|
d && (d.style.display = f ? "" : "none");
|
|
213
213
|
}
|
|
214
214
|
}
|
|
@@ -237,7 +237,7 @@ class S {
|
|
|
237
237
|
* Excludes leaves that are already being flipped.
|
|
238
238
|
*/
|
|
239
239
|
getNextAvailableLeaf(t) {
|
|
240
|
-
if (t ===
|
|
240
|
+
if (t === c.Forward) {
|
|
241
241
|
const [, e] = this.currentOrTurningLeaves;
|
|
242
242
|
if (!e) return;
|
|
243
243
|
for (let i = e.index; i < this.leaves.length; i++) {
|
|
@@ -246,7 +246,7 @@ class S {
|
|
|
246
246
|
return s;
|
|
247
247
|
}
|
|
248
248
|
return;
|
|
249
|
-
} else if (t ===
|
|
249
|
+
} else if (t === c.Backward) {
|
|
250
250
|
const [e] = this.currentOrTurningLeaves;
|
|
251
251
|
if (!e) return;
|
|
252
252
|
for (let i = e.index; i >= 0; i--) {
|
|
@@ -260,18 +260,18 @@ class S {
|
|
|
260
260
|
onDragStart(t) {
|
|
261
261
|
if (!this.isInsideNoFlipZone(t.srcEvent?.target ?? null)) {
|
|
262
262
|
if (this.currentManualFlip) {
|
|
263
|
-
this.pendingFlipDirection =
|
|
263
|
+
this.pendingFlipDirection = c.None, this.pendingFlipStartingPos = 0;
|
|
264
264
|
return;
|
|
265
265
|
}
|
|
266
|
-
this.isDragging = !0, this.clearHoverShadow(), this.pendingFlipStartingPos = t.center.x, this.pendingFlipDirection =
|
|
266
|
+
this.isDragging = !0, this.clearHoverShadow(), this.pendingFlipStartingPos = t.center.x, this.pendingFlipDirection = c.None;
|
|
267
267
|
}
|
|
268
268
|
}
|
|
269
269
|
onDragUpdate(t) {
|
|
270
270
|
let e = this.currentManualFlip;
|
|
271
|
-
const i = t.center.x, s = this.bookElement?.clientWidth ?? 0,
|
|
272
|
-
if (Math.abs(
|
|
273
|
-
const o = this.pendingFlipDirection !==
|
|
274
|
-
if (this.pendingFlipDirection ===
|
|
271
|
+
const i = t.center.x, s = this.bookElement?.clientWidth ?? 0, r = this.isLTR ? this.pendingFlipStartingPos - i : i - this.pendingFlipStartingPos;
|
|
272
|
+
if (Math.abs(r) > s || r === 0) return;
|
|
273
|
+
const o = this.pendingFlipDirection !== c.None ? this.pendingFlipDirection : r > 0 ? c.Forward : c.Backward;
|
|
274
|
+
if (this.pendingFlipDirection === c.None && (this.pendingFlipDirection = o), !e) {
|
|
275
275
|
for (const n of this.activeFlips.values())
|
|
276
276
|
if (n.isDuringAutoFlip && n.direction !== o)
|
|
277
277
|
return;
|
|
@@ -287,16 +287,16 @@ class S {
|
|
|
287
287
|
isDuringAutoFlip: !1
|
|
288
288
|
}, this.activeFlips.set(n.index, e);
|
|
289
289
|
}
|
|
290
|
-
switch (e.delta =
|
|
291
|
-
case
|
|
292
|
-
const n =
|
|
293
|
-
if (n > 1 ||
|
|
290
|
+
switch (e.delta = r, e.direction) {
|
|
291
|
+
case c.Forward: {
|
|
292
|
+
const n = r / s;
|
|
293
|
+
if (n > 1 || r < 0) return;
|
|
294
294
|
e.leaf.efficientFlipToPosition(n);
|
|
295
295
|
break;
|
|
296
296
|
}
|
|
297
|
-
case
|
|
298
|
-
const n = 1 - Math.abs(
|
|
299
|
-
if (n < 0 ||
|
|
297
|
+
case c.Backward: {
|
|
298
|
+
const n = 1 - Math.abs(r) / s;
|
|
299
|
+
if (n < 0 || r > 0) return;
|
|
300
300
|
e.leaf.efficientFlipToPosition(n);
|
|
301
301
|
break;
|
|
302
302
|
}
|
|
@@ -305,22 +305,22 @@ class S {
|
|
|
305
305
|
onDragEnd(t) {
|
|
306
306
|
const e = this.currentManualFlip;
|
|
307
307
|
if (!e) {
|
|
308
|
-
this.pendingFlipDirection =
|
|
308
|
+
this.pendingFlipDirection = c.None, this.pendingFlipStartingPos = 0, this.isDragging = !1, this.clearHoverShadow();
|
|
309
309
|
return;
|
|
310
310
|
}
|
|
311
311
|
const i = t.velocityX * 1e3;
|
|
312
312
|
let s;
|
|
313
313
|
switch (e.direction) {
|
|
314
|
-
case
|
|
314
|
+
case c.Forward:
|
|
315
315
|
(this.isLTR ? i < -this.fastDeltaThreshold : i > this.fastDeltaThreshold) || e.leaf.flipPosition >= 0.5 ? s = 1 : s = 0;
|
|
316
316
|
break;
|
|
317
|
-
case
|
|
317
|
+
case c.Backward:
|
|
318
318
|
(this.isLTR ? i > this.fastDeltaThreshold : i < -this.fastDeltaThreshold) || e.leaf.flipPosition <= 0.5 ? s = 0 : s = 1;
|
|
319
319
|
break;
|
|
320
320
|
default:
|
|
321
321
|
return;
|
|
322
322
|
}
|
|
323
|
-
e.isDuringAutoFlip = !0, this.pendingFlipDirection =
|
|
323
|
+
e.isDuringAutoFlip = !0, this.pendingFlipDirection = c.None, this.pendingFlipStartingPos = 0, this.isDragging = !1, e.leaf.flipToPosition(s).then(() => {
|
|
324
324
|
this.activeFlips.delete(e.leaf.index);
|
|
325
325
|
});
|
|
326
326
|
}
|
|
@@ -344,18 +344,18 @@ class S {
|
|
|
344
344
|
}
|
|
345
345
|
const e = this.bookElement.getBoundingClientRect();
|
|
346
346
|
if (e.width <= 0) return;
|
|
347
|
-
const i = t.clientX - e.left, s = e.width * I,
|
|
348
|
-
if (!
|
|
347
|
+
const i = t.clientX - e.left, s = e.width * I, r = i <= s, o = i >= e.width - s;
|
|
348
|
+
if (!r && !o) {
|
|
349
349
|
this.clearHoverShadow();
|
|
350
350
|
return;
|
|
351
351
|
}
|
|
352
|
-
const n = this.isLTR ? o :
|
|
352
|
+
const n = this.isLTR ? o : r, l = n ? c.Forward : c.Backward, f = this.getNextAvailableLeaf(l);
|
|
353
353
|
if (!f) {
|
|
354
354
|
this.clearHoverShadow();
|
|
355
355
|
return;
|
|
356
356
|
}
|
|
357
|
-
const d = n ? this.isLTR ? e.width - i : i : this.isLTR ? i : e.width - i,
|
|
358
|
-
this.hoveredLeaf && this.hoveredLeaf !== f && this.hoveredLeaf.setHoverShadow(0), this.hoveredLeaf = f, f.setHoverShadow(
|
|
357
|
+
const d = n ? this.isLTR ? e.width - i : i : this.isLTR ? i : e.width - i, h = (1 - Math.min(1, d / s)) * $;
|
|
358
|
+
this.hoveredLeaf && this.hoveredLeaf !== f && this.hoveredLeaf.setHoverShadow(0), this.hoveredLeaf = f, f.setHoverShadow(h);
|
|
359
359
|
});
|
|
360
360
|
handleMouseLeave = () => {
|
|
361
361
|
this.clearHoverShadow();
|
|
@@ -365,8 +365,8 @@ class S {
|
|
|
365
365
|
}
|
|
366
366
|
onTurned(t, e) {
|
|
367
367
|
for (let i = 0; i < this.pageElements.length; i++) {
|
|
368
|
-
const s = this.pageElements[i],
|
|
369
|
-
(
|
|
368
|
+
const s = this.pageElements[i], r = t.includes(i), o = e?.includes(i);
|
|
369
|
+
(r ? s.classList.add : !e || !o ? () => null : s.classList.remove).bind(s.classList)("current-page");
|
|
370
370
|
}
|
|
371
371
|
this.updateLeavesBufferVisibility(), this.onPageChanged && this.onPageChanged(this.currentPageIndex);
|
|
372
372
|
}
|
|
@@ -410,13 +410,13 @@ class S {
|
|
|
410
410
|
* @returns Promise that resolves when the flip animation completes
|
|
411
411
|
*/
|
|
412
412
|
async flipNext() {
|
|
413
|
-
if (this.hasActiveAutoFlipInDirection(
|
|
413
|
+
if (this.hasActiveAutoFlipInDirection(c.Backward))
|
|
414
414
|
return;
|
|
415
|
-
const t = this.getNextAvailableLeaf(
|
|
415
|
+
const t = this.getNextAvailableLeaf(c.Forward);
|
|
416
416
|
if (!t) return;
|
|
417
417
|
const e = {
|
|
418
418
|
leaf: t,
|
|
419
|
-
direction:
|
|
419
|
+
direction: c.Forward,
|
|
420
420
|
startingPos: 0,
|
|
421
421
|
delta: 0,
|
|
422
422
|
isDuringAutoFlip: !0
|
|
@@ -433,13 +433,13 @@ class S {
|
|
|
433
433
|
* @returns Promise that resolves when the flip animation completes
|
|
434
434
|
*/
|
|
435
435
|
async flipPrev() {
|
|
436
|
-
if (this.hasActiveAutoFlipInDirection(
|
|
436
|
+
if (this.hasActiveAutoFlipInDirection(c.Forward))
|
|
437
437
|
return;
|
|
438
|
-
const t = this.getNextAvailableLeaf(
|
|
438
|
+
const t = this.getNextAvailableLeaf(c.Backward);
|
|
439
439
|
if (!t) return;
|
|
440
440
|
const e = {
|
|
441
441
|
leaf: t,
|
|
442
|
-
direction:
|
|
442
|
+
direction: c.Backward,
|
|
443
443
|
startingPos: 0,
|
|
444
444
|
delta: 0,
|
|
445
445
|
isDuringAutoFlip: !0
|
|
@@ -484,15 +484,19 @@ class S {
|
|
|
484
484
|
);
|
|
485
485
|
return;
|
|
486
486
|
}
|
|
487
|
-
const e =
|
|
487
|
+
const e = t === this.pagesCount - 1, i = t % 2 === 1, s = e && i;
|
|
488
|
+
let r;
|
|
489
|
+
t <= 0 ? r = -1 : s ? r = this.leaves.length - 1 : r = Math.floor((t - 1) / 2);
|
|
488
490
|
for (let o = 0; o < this.leaves.length; o++) {
|
|
489
|
-
const n = this.leaves[o],
|
|
490
|
-
|
|
491
|
+
const n = this.leaves[o], l = o <= r;
|
|
492
|
+
l && !n.isTurned ? (n.flipPosition = 1, this.applyLeafTransform(n, 1)) : !l && n.isTurned && (n.flipPosition = 0, this.applyLeafTransform(n, 0));
|
|
491
493
|
}
|
|
492
|
-
if (
|
|
494
|
+
if (s)
|
|
493
495
|
this.prevVisiblePageIndices = [t];
|
|
496
|
+
else if (r < 0)
|
|
497
|
+
this.prevVisiblePageIndices = 1 < this.pagesCount ? [0, 1] : [0];
|
|
494
498
|
else {
|
|
495
|
-
const o =
|
|
499
|
+
const o = r * 2 + 1;
|
|
496
500
|
this.prevVisiblePageIndices = o + 1 < this.pagesCount ? [o, o + 1] : [o];
|
|
497
501
|
}
|
|
498
502
|
for (let o = 0; o < this.pageElements.length; o++) {
|
package/dist/flipbook.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flipbook.js","sources":["../src/flip-direction.ts","../src/leaf.ts","../src/aspect-ratio.ts","../src/size.ts","../src/flipbook.ts"],"sourcesContent":["export enum FlipDirection {\n\tForward = \"Forward\",\n\tBackward = \"Backward\",\n\tNone = \"None\",\n}\n","import { throttle } from \"throttle-debounce\";\nimport type { IntRange } from \"type-fest\";\nimport { FlipDirection } from \"./flip-direction\";\n\n/** Multiplier for shadow intensity (slightly stronger than base) */\nconst SHADOW_STRENGTH_MULTIPLIER = 1.1;\n/** Multiplier for highlight intensity (slightly weaker than base) */\nconst HIGHLIGHT_STRENGTH_MULTIPLIER = 0.9;\n/** Maximum lift in pixels for the shadow effect */\nconst SHADOW_LIFT_PX = 8;\n\n// number between 1 to infinity\nexport type DegreesPerSecond = IntRange<1, typeof Infinity>;\nexport type FlipPosition = IntRange<0, 2>;\nexport const FLIPPED = true;\nexport const NOT_FLIPPED = false;\nexport class Leaf {\n\tprivate currentAnimation: Promise<void> | null = null;\n\tprivate targetFlipPosition: FlipPosition | null = null;\n\tprivate wrappedFlipPosition: number;\n\tprivate hoverShadow = 0;\n\n\tconstructor(\n\t\treadonly index: number,\n\t\treadonly pages: [HTMLElement, HTMLElement | undefined],\n\t\tisFlipped: boolean,\n\t\tprivate readonly bookProperties: {\n\t\t\tisLTR: boolean;\n\t\t\tpagesCount: number;\n\t\t\tleavesCount: number;\n\t\t},\n\t\tprivate readonly onTurned: (direction: FlipDirection) => void,\n\t) {\n\t\tthis.wrappedFlipPosition = isFlipped ? 1 : 0;\n\t\t// TODO: rethink this\n\t\t// if(isFlipped) {\n\t\t// // this.flipToPosition(1);\n\t\t// }\n\t}\n\n\tget isTurned(): boolean {\n\t\treturn this.flipPosition === 1;\n\t}\n\tget isTurning(): boolean {\n\t\treturn this.flipPosition !== 0;\n\t}\n\tget isCover(): boolean {\n\t\treturn this.isFirst || this.isLast;\n\t}\n\tget isFirst(): boolean {\n\t\treturn this.index === 0;\n\t}\n\tget isLast(): boolean {\n\t\treturn this.index === this.bookProperties.leavesCount - 1;\n\t}\n\tset flipPosition(value: number) {\n\t\tthis.wrappedFlipPosition = Math.max(0, Math.min(1, value)) as FlipPosition;\n\t}\n\tget flipPosition(): number {\n\t\treturn this.wrappedFlipPosition;\n\t}\n\n\tsetHoverShadow(progress: number): void {\n\t\tconst clamped = Math.max(0, Math.min(1, progress));\n\t\tif (this.hoverShadow === clamped) return;\n\t\tthis.hoverShadow = clamped;\n\t\tthis.applyTransform(this.flipPosition);\n\t}\n\n\tasync flipToPosition(\n\t\tflipPosition: FlipPosition,\n\t\tvelocity: DegreesPerSecond = 225 as DegreesPerSecond,\n\t) {\n\t\tif (this.currentAnimation) {\n\t\t\tawait this.currentAnimation;\n\t\t}\n\n\t\tif (this.flipPosition === flipPosition) {\n\t\t\treturn Promise.resolve();\n\t\t}\n\n\t\tif (this.targetFlipPosition === flipPosition) {\n\t\t\treturn this.currentAnimation ?? Promise.resolve();\n\t\t}\n\n\t\tthis.targetFlipPosition = flipPosition;\n\n\t\tthis.currentAnimation = new Promise<void>((resolve) => {\n\t\t\tconst currentFlipPosition = this.flipPosition;\n\t\t\tconst distance = Math.abs(flipPosition - currentFlipPosition);\n\t\t\tconst duration = ((distance * 180) / velocity) * 1000; // duration in milliseconds\n\n\t\t\tconst start = performance.now();\n\t\t\tconst step = (timestamp: number) => {\n\t\t\t\tconst elapsed = timestamp - start;\n\n\t\t\t\tif (elapsed < 0) {\n\t\t\t\t\trequestAnimationFrame(step);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst progress = Math.min(elapsed / duration, 1);\n\t\t\t\tconst newPosition = currentFlipPosition + progress * (flipPosition - currentFlipPosition);\n\n\t\t\t\tthis.applyTransform(newPosition);\n\n\t\t\t\t// Ensure the new position is within valid bounds [0, 1]\n\t\t\t\tthis.flipPosition = Math.max(0, Math.min(1, newPosition)) as FlipPosition;\n\t\t\t\tif (this.flipPosition === 1 || this.flipPosition === 0) {\n\t\t\t\t\tthis.onTurned(this.flipPosition === 1 ? FlipDirection.Forward : FlipDirection.Backward);\n\t\t\t\t}\n\t\t\t\t// Detailed log for debugging\n\t\t\t\t// console.log(\n\t\t\t\t// `Timestamp: ${timestamp}, Elapsed: ${elapsed}, Progress: ${progress}, Current Position: ${currentFlipPosition}, Requested Position: ${flipPosition}, New Position: ${this.flipPosition}`\n\t\t\t\t// );\n\n\t\t\t\tif (progress < 1) {\n\t\t\t\t\trequestAnimationFrame(step);\n\t\t\t\t} else {\n\t\t\t\t\tthis.currentAnimation = null;\n\t\t\t\t\tthis.targetFlipPosition = null;\n\t\t\t\t\tresolve();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\trequestAnimationFrame(step);\n\t\t});\n\n\t\treturn this.currentAnimation;\n\t}\n\n\tasync efficientFlipToPosition(\n\t\tflipPosition: FlipPosition,\n\t\tvelocity: DegreesPerSecond = 20000 as DegreesPerSecond,\n\t) {\n\t\treturn throttle(1, this.flipToPosition.bind(this))(flipPosition, velocity);\n\t}\n\n\tapplyTransform(position: number): void {\n\t\tconst clampedPosition = Math.max(0, Math.min(1, position));\n\t\tconst shadowFromFlip = Math.sin(clampedPosition * Math.PI);\n\t\tconst shadowProgress = Math.max(shadowFromFlip, this.hoverShadow);\n\t\tconst shadowStrength = Math.min(1, shadowProgress * SHADOW_STRENGTH_MULTIPLIER);\n\t\tconst highlightStrength = Math.min(1, shadowProgress * HIGHLIGHT_STRENGTH_MULTIPLIER);\n\t\tconst lift = `${(shadowProgress * SHADOW_LIFT_PX).toFixed(3)}px`;\n\n\t\tthis.pages.forEach((page, index) => {\n\t\t\tconst isLTR = this.bookProperties.isLTR;\n\t\t\tif (!page) return;\n\n\t\t\tconst isOdd = (index % 2) + 1 === 1;\n\t\t\tconst degrees = isOdd\n\t\t\t\t? isLTR\n\t\t\t\t\t? clampedPosition > 0.5\n\t\t\t\t\t\t? 180 - clampedPosition * 180\n\t\t\t\t\t\t: -clampedPosition * 180\n\t\t\t\t\t: clampedPosition > 0.5\n\t\t\t\t\t\t? -(180 - clampedPosition * 180)\n\t\t\t\t\t\t: clampedPosition * 180\n\t\t\t\t: isLTR\n\t\t\t\t\t? clampedPosition < 0.5\n\t\t\t\t\t\t? -clampedPosition * 180\n\t\t\t\t\t\t: 180 - clampedPosition * 180\n\t\t\t\t\t: clampedPosition < 0.5\n\t\t\t\t\t\t? clampedPosition * 180\n\t\t\t\t\t\t: -(180 - clampedPosition * 180);\n\t\t\tconst rotateY = `${degrees}deg`;\n\t\t\tconst translateX = `${isOdd ? (isLTR ? \"100%\" : \"-100%\") : \"0px\"}`;\n\t\t\tconst scaleX = isOdd ? (clampedPosition > 0.5 ? -1 : 1) : clampedPosition < 0.5 ? -1 : 1;\n\t\t\tconst origin = isOdd ? (isLTR ? \"left\" : \"right\") : isLTR ? \"right\" : \"left\";\n\t\t\tconst edge = origin === \"left\" ? \"right\" : \"left\";\n\n\t\t\tpage.style.transform = `translateX(${translateX})rotateY(${rotateY})scaleX(${scaleX})`;\n\t\t\tpage.style.transformOrigin = origin;\n\t\t\tpage.style.zIndex = `${\n\t\t\t\tclampedPosition > 0.5\n\t\t\t\t\t? page.dataset.pageIndex\n\t\t\t\t\t: this.bookProperties.pagesCount - (page.dataset.pageIndex as unknown as number)\n\t\t\t}`;\n\t\t\tpage.style.setProperty(\"--inner-shadow-shadow\", shadowStrength.toFixed(3));\n\t\t\tpage.style.setProperty(\"--inner-shadow-highlight\", highlightStrength.toFixed(3));\n\t\t\tpage.style.setProperty(\"--inner-shadow-lift\", lift);\n\t\t\tpage.style.setProperty(\"--inner-shadow-edge\", edge);\n\t\t});\n\t}\n}\n","export interface AspectRatio {\n\twidth: number;\n\theight: number;\n}\nexport class AspectRatioImpl implements AspectRatio {\n\tstatic from(aspectRatio: AspectRatio) {\n\t\treturn new AspectRatioImpl(aspectRatio.width, aspectRatio.height);\n\t}\n\tconstructor(\n\t\tpublic readonly width: number,\n\t\tpublic readonly height: number,\n\t) {}\n\tget value(): number {\n\t\treturn this.width / this.height;\n\t}\n}\n","import { type AspectRatio, AspectRatioImpl } from \"./aspect-ratio\";\n\nexport class Size {\n\taspectRatio: AspectRatioImpl;\n\taspectRatioFit(rhsAspectRatio: AspectRatio) {\n\t\tconst rhsAspectRatioValue = AspectRatioImpl.from(rhsAspectRatio).value;\n\t\treturn this.aspectRatio.value > rhsAspectRatioValue\n\t\t\t? new Size(this.height * rhsAspectRatioValue, this.height)\n\t\t\t: new Size(this.width, this.width / rhsAspectRatioValue);\n\t}\n\tconstructor(\n\t\tpublic readonly width: number,\n\t\tpublic readonly height: number,\n\t) {\n\t\tthis.aspectRatio = new AspectRatioImpl(width, height);\n\t}\n\tget asString(): string {\n\t\treturn `${this.width}x${this.height}`;\n\t}\n}\n","import \"./pages.scss\";\nimport \"./flipbook.scss\";\nimport Hammer from \"hammerjs\";\nimport { throttle } from \"throttle-debounce\";\nimport type { AspectRatio } from \"./aspect-ratio\";\nimport type { FlipBookOptions } from \"./flip-book-options\";\nimport { FlipDirection } from \"./flip-direction\";\nimport { type FlipPosition, Leaf } from \"./leaf\";\nimport type { PageSemantics } from \"./page-semantics\";\nimport { Size } from \"./size\";\n\n/** Default threshold for fast flip detection (in ms) */\nconst DEFAULT_FAST_DELTA = 500;\n\n/** Percentage of book width that triggers hover shadow effect */\nconst EDGE_ZONE_RATIO = 0.18;\n/** Maximum hover shadow strength (0-1) */\nconst HOVER_STRENGTH_MAX = 0.12;\n/** Throttle interval for mouse move handler in ms */\nconst MOUSE_MOVE_THROTTLE_MS = 16;\n\n/** State for a single flip operation - enables concurrent page flipping */\ninterface FlipState {\n\tleaf: Leaf;\n\tdirection: FlipDirection;\n\tstartingPos: number;\n\tdelta: number;\n\tisDuringAutoFlip: boolean;\n}\n\n/**\n * Core FlipBook class for creating realistic page-flip animations.\n *\n * @example\n * ```typescript\n * import { FlipBook } from 'html-flip-book-vanilla';\n *\n * const flipBook = new FlipBook({\n * pagesCount: 10,\n * direction: 'ltr',\n * });\n *\n * flipBook.render('#book-container');\n *\n * // Navigate programmatically\n * await flipBook.flipNext();\n * await flipBook.goToPage(5);\n * ```\n */\nclass FlipBook {\n\tbookElement?: HTMLElement;\n\tprivate pageElements: HTMLElement[] = [];\n\tprivate readonly pagesCount: number;\n\tprivate readonly leafAspectRatio: AspectRatio = { width: 2, height: 3 };\n\tprivate readonly coverAspectRatio: AspectRatio = {\n\t\twidth: 2.15,\n\t\theight: 3.15,\n\t};\n\tprivate readonly direction: \"rtl\" | \"ltr\" = \"ltr\";\n\tprivate readonly fastDeltaThreshold: number = DEFAULT_FAST_DELTA;\n\tprivate readonly initialTurnedLeaves: Set<number> = new Set();\n\tprivate readonly onPageChanged?: (pageIndex: number) => void;\n\tprivate readonly pageSemantics: PageSemantics | undefined;\n\tprivate readonly leavesBuffer?: number;\n\tprivate leaves: Leaf[] = [];\n\t// flipping state - supports concurrent page flipping\n\tprivate activeFlips: Map<number, FlipState> = new Map();\n\tprivate pendingFlipStartingPos = 0;\n\tprivate pendingFlipDirection: FlipDirection = FlipDirection.None;\n\tprivate isDragging = false;\n\tprivate hoveredLeaf: Leaf | undefined;\n\ttouchStartingPos = { x: 0, y: 0 };\n\t/** True when the current touch started inside a [data-flipbook-no-flip] element (e.g. carousel). */\n\tprivate touchStartedInNoFlipZone = false;\n\tprivate prevVisiblePageIndices: [number] | [number, number] | undefined;\n\t// Hammer instance for cleanup\n\tprivate hammer: HammerManager | undefined;\n\n\tprivate static readonly NO_FLIP_SELECTOR = \"[data-flipbook-no-flip]\";\n\n\tprivate isInsideNoFlipZone(el: EventTarget | null): boolean {\n\t\treturn el instanceof Element && el.closest(FlipBook.NO_FLIP_SELECTOR) != null;\n\t}\n\tprivate get isLTR(): boolean {\n\t\treturn this.direction === \"ltr\";\n\t}\n\n\tprivate get currentOrTurningLeaves(): [Leaf | undefined, Leaf | undefined] {\n\t\tlet secondLeafIndex = -1;\n\t\tfor (let i = this.leaves.length - 1; i >= 0; i--) {\n\t\t\tconst leaf = this.leaves[i];\n\t\t\tif (leaf.isTurned || leaf.isTurning) {\n\t\t\t\tsecondLeafIndex = leaf.index + 1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn secondLeafIndex === -1\n\t\t\t? [undefined, this.leaves[0]]\n\t\t\t: secondLeafIndex === this.leaves.length\n\t\t\t\t? [this.leaves[secondLeafIndex - 1], undefined]\n\t\t\t\t: [this.leaves[secondLeafIndex - 1], this.leaves[secondLeafIndex]];\n\t}\n\n\tconstructor(options: FlipBookOptions) {\n\t\tthis.pagesCount = options.pagesCount;\n\t\tthis.leafAspectRatio = options.leafAspectRatio || this.leafAspectRatio;\n\t\tthis.coverAspectRatio = options.coverAspectRatio || this.coverAspectRatio;\n\t\tthis.direction = options.direction || this.direction;\n\t\tthis.fastDeltaThreshold = options.fastDeltaThreshold ?? this.fastDeltaThreshold;\n\t\tthis.initialTurnedLeaves = new Set(options.initialTurnedLeaves ?? []);\n\t\tthis.pageSemantics = options.pageSemantics;\n\t\tthis.onPageChanged = options.onPageChanged;\n\t\tthis.leavesBuffer = options.leavesBuffer;\n\t}\n\n\trender(selector: string, debug = false) {\n\t\tconst bookElement = document.querySelector(selector);\n\t\tif (!bookElement) {\n\t\t\tthrow new Error(`Couldn't find container with selector: ${selector}`);\n\t\t}\n\t\tthis.bookElement = bookElement as HTMLElement;\n\t\tif (!this.bookElement.classList.contains(\"flipbook\")) {\n\t\t\tthis.bookElement.classList.add(\"flipbook\");\n\t\t}\n\n\t\tconst pageElements = bookElement.querySelectorAll(\".page\");\n\t\tif (!pageElements.length) {\n\t\t\tthrow new Error(\"No pages found in flipbook\");\n\t\t}\n\t\tthis.pageElements = Array.from(pageElements) as HTMLElement[];\n\t\tthis.leaves.splice(0, this.leaves.length);\n\t\tconst leavesCount = Math.ceil(this.pagesCount / 2);\n\t\tconst maxCoverSize = new Size(this.bookElement.clientWidth / 2, this.bookElement.clientHeight);\n\t\tconst coverSize = maxCoverSize.aspectRatioFit(this.coverAspectRatio);\n\t\tconst leafSize = new Size(\n\t\t\t(coverSize.width * this.leafAspectRatio.width) / this.coverAspectRatio.width,\n\t\t\t(coverSize.height * this.leafAspectRatio.height) / this.coverAspectRatio.height,\n\t\t);\n\t\tthis.bookElement.style.perspective = `${Math.min(leafSize.width * 2, leafSize.height) * 2}px`;\n\t\tthis.pageElements.forEach((pageElement, pageIndex) => {\n\t\t\tpageElement.style.width = `${leafSize.width}px`;\n\t\t\tpageElement.style.height = `${leafSize.height}px`;\n\n\t\t\tpageElement.style.zIndex = `${this.pagesCount - pageIndex}`;\n\t\t\tpageElement.dataset.pageIndex = pageIndex.toString();\n\t\t\tpageElement.style[this.isLTR ? \"left\" : \"right\"] =\n\t\t\t\t`${(bookElement.clientWidth - 2 * leafSize.width) / 2}px`;\n\t\t\tpageElement.style.top = `${(bookElement.clientHeight - leafSize.height) / 2}px`;\n\t\t\tpageElement.dataset.pageSemanticName =\n\t\t\t\tthis.pageSemantics?.indexToSemanticName(pageIndex) ?? \"\";\n\t\t\tpageElement.dataset.pageTitle = this.pageSemantics?.indexToTitle(pageIndex) ?? \"\";\n\n\t\t\tconst leafIndex = Math.floor(pageIndex / 2);\n\t\t\tconst isOddPage = (pageIndex + 1) % 2 === 1;\n\t\t\tconst isInitiallyTurned = this.initialTurnedLeaves.has(leafIndex);\n\n\t\t\t// Determine current page based on initial turned leaves\n\t\t\t// Current pages are the first two visible pages after all initially turned leaves\n\t\t\tconst firstVisibleLeafIndex =\n\t\t\t\tthis.initialTurnedLeaves.size > 0 ? Math.max(...this.initialTurnedLeaves) + 1 : 0;\n\t\t\tconst isCurrentPage =\n\t\t\t\tleafIndex === firstVisibleLeafIndex ||\n\t\t\t\t(leafIndex === firstVisibleLeafIndex - 1 && isInitiallyTurned);\n\n\t\t\tpageElement.classList.add(\n\t\t\t\tisOddPage ? \"odd\" : \"even\",\n\t\t\t\t...(isCurrentPage ? [\"current-page\"] : []),\n\t\t\t);\n\t\t\tif (isOddPage) {\n\t\t\t\t// Apply correct initial transform based on turned state\n\t\t\t\tif (isInitiallyTurned) {\n\t\t\t\t\t// Fully turned: rotateY(180) or similar based on direction\n\t\t\t\t\tconst scaleX = -1;\n\t\t\t\t\tconst transform = `translateX(${this.isLTR ? \"100%\" : \"-100%\"})rotateY(${this.isLTR ? \"180deg\" : \"-180deg\"})scaleX(${scaleX})`;\n\t\t\t\t\tpageElement.style.transform = transform;\n\t\t\t\t\tpageElement.style.transformOrigin = this.isLTR ? \"left\" : \"right\";\n\t\t\t\t\tpageElement.style.zIndex = `${pageIndex}`; // Turned pages have lower z-index\n\t\t\t\t} else {\n\t\t\t\t\tpageElement.style.transform = `translateX(${this.isLTR ? `` : `-`}100%)`;\n\t\t\t\t\tpageElement.style.zIndex = `${this.pagesCount - pageIndex}`;\n\t\t\t\t}\n\n\t\t\t\tthis.leaves[leafIndex] = new Leaf(\n\t\t\t\t\tleafIndex,\n\t\t\t\t\t[pageElement, undefined],\n\t\t\t\t\tisInitiallyTurned,\n\t\t\t\t\t{\n\t\t\t\t\t\tisLTR: this.isLTR,\n\t\t\t\t\t\tleavesCount: leavesCount,\n\t\t\t\t\t\tpagesCount: this.pagesCount,\n\t\t\t\t\t},\n\t\t\t\t\t(direction: FlipDirection) => {\n\t\t\t\t\t\tconst currentVisiblePageIndices: [number] | [number, number] =\n\t\t\t\t\t\t\tdirection === FlipDirection.Forward\n\t\t\t\t\t\t\t\t? pageIndex + 2 === this.pagesCount\n\t\t\t\t\t\t\t\t\t? [pageIndex + 1]\n\t\t\t\t\t\t\t\t\t: [pageIndex + 1, pageIndex + 2]\n\t\t\t\t\t\t\t\t: pageIndex === 0\n\t\t\t\t\t\t\t\t\t? [pageIndex]\n\t\t\t\t\t\t\t\t\t: [pageIndex - 1, pageIndex];\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tthis.prevVisiblePageIndices &&\n\t\t\t\t\t\t\tthis.prevVisiblePageIndices.length === currentVisiblePageIndices.length &&\n\t\t\t\t\t\t\tcurrentVisiblePageIndices.every((v, i) => v === this.prevVisiblePageIndices?.[i])\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst prevVisiblePageIndices = this.prevVisiblePageIndices;\n\t\t\t\t\t\tthis.prevVisiblePageIndices = currentVisiblePageIndices;\n\n\t\t\t\t\t\t// TODO expose to outside using https://github.com/open-draft/strict-event-emitter, and just be a consumer internally\n\t\t\t\t\t\tthis.onTurned(currentVisiblePageIndices, prevVisiblePageIndices);\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t// Even page (back side of leaf)\n\t\t\t\tif (isInitiallyTurned) {\n\t\t\t\t\t// Fully turned: apply matching transform for back side\n\t\t\t\t\tconst scaleX = 1;\n\t\t\t\t\tpageElement.style.transform = `translateX(0px)rotateY(${this.isLTR ? \"180deg\" : \"-180deg\"})scaleX(${scaleX})`;\n\t\t\t\t\tpageElement.style.transformOrigin = this.isLTR ? \"right\" : \"left\";\n\t\t\t\t\tpageElement.style.zIndex = `${pageIndex}`; // Turned pages have lower z-index\n\t\t\t\t} else {\n\t\t\t\t\tpageElement.style.transform = `scaleX(-1)translateX(${this.isLTR ? `-` : ``}100%)`;\n\t\t\t\t\tpageElement.style.zIndex = `${this.pagesCount - pageIndex}`;\n\t\t\t\t}\n\t\t\t\tthis.leaves[leafIndex].pages[1] = pageElement;\n\t\t\t}\n\t\t});\n\n\t\t// Set initial visible page indices based on initially turned leaves\n\t\tconst firstVisibleLeafIndex =\n\t\t\tthis.initialTurnedLeaves.size > 0 ? Math.max(...this.initialTurnedLeaves) + 1 : 0;\n\t\tconst firstVisiblePageIndex = firstVisibleLeafIndex * 2;\n\n\t\t// Handle edge case where all leaves are turned (showing last page only)\n\t\tif (firstVisiblePageIndex >= this.pagesCount) {\n\t\t\t// All leaves are turned - show the last page(s)\n\t\t\tconst lastLeafIndex = Math.ceil(this.pagesCount / 2) - 1;\n\t\t\tconst lastLeafFirstPage = lastLeafIndex * 2;\n\t\t\tthis.prevVisiblePageIndices =\n\t\t\t\tlastLeafFirstPage + 1 < this.pagesCount\n\t\t\t\t\t? [lastLeafFirstPage, lastLeafFirstPage + 1]\n\t\t\t\t\t: [lastLeafFirstPage];\n\t\t} else {\n\t\t\tthis.prevVisiblePageIndices =\n\t\t\t\tfirstVisiblePageIndex + 1 < this.pagesCount\n\t\t\t\t\t? [firstVisiblePageIndex, firstVisiblePageIndex + 1]\n\t\t\t\t\t: [firstVisiblePageIndex];\n\t\t}\n\n\t\tthis.hammer = new Hammer(this.bookElement);\n\t\tthis.hammer.on(\"panstart\", this.onDragStart.bind(this));\n\t\tthis.hammer.on(\"panmove\", this.onDragUpdate.bind(this));\n\t\tthis.hammer.on(\"panend\", this.onDragEnd.bind(this));\n\t\tthis.bookElement.addEventListener(\"touchstart\", this.handleTouchStart.bind(this), {\n\t\t\tpassive: false,\n\t\t});\n\t\tthis.bookElement.addEventListener(\"touchmove\", this.handleTouchMove.bind(this), {\n\t\t\tpassive: false,\n\t\t});\n\t\tthis.bookElement.addEventListener(\n\t\t\t\"mousemove\",\n\t\t\tthis.handleMouseMove as unknown as EventListener,\n\t\t);\n\t\tthis.bookElement.addEventListener(\"mouseleave\", this.handleMouseLeave as EventListener);\n\t\t// Apply initial leaves buffer visibility\n\t\tthis.updateLeavesBufferVisibility();\n\t\tif (debug) this.fillDebugBar();\n\t}\n\n\t/**\n\t * Update visibility of leaves based on the buffer setting.\n\t * Leaves outside the buffer range are hidden for performance.\n\t */\n\tprivate updateLeavesBufferVisibility(): void {\n\t\tif (this.leavesBuffer === undefined) {\n\t\t\treturn; // No buffer - all leaves stay visible\n\t\t}\n\n\t\tconst leavesCount = this.leaves.length;\n\t\tif (leavesCount === 0) return;\n\n\t\t// Find the current leaf index (the first non-turned leaf, or last leaf if all turned)\n\t\tconst [leftLeaf, rightLeaf] = this.currentOrTurningLeaves;\n\t\tconst currentLeafIndex = rightLeaf?.index ?? leftLeaf?.index ?? 0;\n\n\t\t// Calculate buffer range\n\t\tconst bufferStart = Math.max(0, currentLeafIndex - this.leavesBuffer);\n\t\tconst bufferEnd = Math.min(leavesCount - 1, currentLeafIndex + this.leavesBuffer);\n\n\t\t// Update visibility of all leaves\n\t\tfor (let i = 0; i < leavesCount; i++) {\n\t\t\tconst leaf = this.leaves[i];\n\t\t\tconst isWithinBuffer = i >= bufferStart && i <= bufferEnd;\n\n\t\t\t// Update both pages of the leaf\n\t\t\tfor (const page of leaf.pages) {\n\t\t\t\tif (page) {\n\t\t\t\t\tpage.style.display = isWithinBuffer ? \"\" : \"none\";\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate fillDebugBar() {\n\t\tconst debugBar = document.createElement(\"div\");\n\t\tdebugBar.className = \"flipbook-debug-bar\";\n\t\tthis.bookElement?.appendChild(debugBar);\n\t\tsetInterval(() => {\n\t\t\t// Populate debug bar with relevant information\n\t\t\tconst activeFlipsInfo = Array.from(this.activeFlips.entries())\n\t\t\t\t.map(([idx, state]) => `${idx}:${state.leaf.flipPosition.toFixed(2)}`)\n\t\t\t\t.join(\", \");\n\t\t\tdebugBar.innerHTML = `\n <div>Direction: ${this.isLTR ? \"LTR\" : \"RTL\"}</div>\n <div>Active Flips: ${this.activeFlips.size} [${activeFlipsInfo}]</div>\n <div>Pending Flip dir: ${this.pendingFlipDirection}</div>\n `;\n\t\t}, 10);\n\t}\n\n\t/**\n\t * Get the current manually-controlled flip state (the one being dragged).\n\t * Returns undefined if no manual flip is in progress.\n\t */\n\tprivate get currentManualFlip(): FlipState | undefined {\n\t\tfor (const state of this.activeFlips.values()) {\n\t\t\tif (!state.isDuringAutoFlip) {\n\t\t\t\treturn state;\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Find the next available leaf for flipping in the given direction.\n\t * Excludes leaves that are already being flipped.\n\t */\n\tprivate getNextAvailableLeaf(direction: FlipDirection): Leaf | undefined {\n\t\tif (direction === FlipDirection.Forward) {\n\t\t\t// Find the rightmost leaf that's not already flipping\n\t\t\t// Start from the current right leaf and go forward\n\t\t\tconst [, rightLeaf] = this.currentOrTurningLeaves;\n\t\t\tif (!rightLeaf) return undefined;\n\n\t\t\t// Find a leaf that's not in activeFlips\n\t\t\tfor (let i = rightLeaf.index; i < this.leaves.length; i++) {\n\t\t\t\tconst leaf = this.leaves[i];\n\t\t\t\tif (!this.activeFlips.has(leaf.index) && !leaf.isTurned) {\n\t\t\t\t\treturn leaf;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn undefined;\n\t\t} else if (direction === FlipDirection.Backward) {\n\t\t\t// Find the leftmost leaf that's not already flipping\n\t\t\tconst [leftLeaf] = this.currentOrTurningLeaves;\n\t\t\tif (!leftLeaf) return undefined;\n\n\t\t\t// Find a leaf that's not in activeFlips\n\t\t\tfor (let i = leftLeaf.index; i >= 0; i--) {\n\t\t\t\tconst leaf = this.leaves[i];\n\t\t\t\tif (!this.activeFlips.has(leaf.index) && (leaf.isTurned || leaf.isTurning)) {\n\t\t\t\t\treturn leaf;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tprivate onDragStart(event: HammerInput) {\n\t\t// Do not start flip when gesture begins inside a no-flip zone (e.g. horizontal carousel)\n\t\tif (this.isInsideNoFlipZone(event.srcEvent?.target ?? null)) {\n\t\t\treturn;\n\t\t}\n\t\t// Allow starting a new flip even if others are in auto-flip mode\n\t\t// Only block if we already have a manual flip in progress\n\t\tif (this.currentManualFlip) {\n\t\t\tthis.pendingFlipDirection = FlipDirection.None;\n\t\t\tthis.pendingFlipStartingPos = 0;\n\t\t\treturn;\n\t\t}\n\t\tthis.isDragging = true;\n\t\tthis.clearHoverShadow();\n\t\tthis.pendingFlipStartingPos = event.center.x;\n\t\tthis.pendingFlipDirection = FlipDirection.None;\n\t}\n\n\tprivate onDragUpdate(event: HammerInput) {\n\t\t// Get or create the current manual flip state\n\t\tlet flipState = this.currentManualFlip;\n\n\t\tconst currentPos = event.center.x;\n\t\tconst bookWidth = this.bookElement?.clientWidth ?? 0;\n\n\t\t// Calculate delta\n\t\tconst delta = this.isLTR\n\t\t\t? this.pendingFlipStartingPos - currentPos\n\t\t\t: currentPos - this.pendingFlipStartingPos;\n\n\t\tif (Math.abs(delta) > bookWidth) return;\n\t\tif (delta === 0) return;\n\n\t\t// Determine direction on first meaningful movement\n\t\tconst direction =\n\t\t\tthis.pendingFlipDirection !== FlipDirection.None\n\t\t\t\t? this.pendingFlipDirection\n\t\t\t\t: delta > 0\n\t\t\t\t\t? FlipDirection.Forward\n\t\t\t\t\t: FlipDirection.Backward;\n\n\t\t// Lock in direction for this flip\n\t\tif (this.pendingFlipDirection === FlipDirection.None) {\n\t\t\tthis.pendingFlipDirection = direction;\n\t\t}\n\n\t\t// Block starting a new flip in opposite direction while auto-flip is in progress\n\t\t// This prevents visual glitches when swiping backward while a forward flip is animating\n\t\tif (!flipState) {\n\t\t\tfor (const state of this.activeFlips.values()) {\n\t\t\t\tif (state.isDuringAutoFlip && state.direction !== direction) {\n\t\t\t\t\t// There's an auto-flip in the opposite direction - block this new flip\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// If we don't have a flip state yet, create one\n\t\tif (!flipState) {\n\t\t\tconst leaf = this.getNextAvailableLeaf(direction);\n\t\t\tif (!leaf) return;\n\n\t\t\tflipState = {\n\t\t\t\tleaf,\n\t\t\t\tdirection,\n\t\t\t\tstartingPos: this.pendingFlipStartingPos,\n\t\t\t\tdelta: 0,\n\t\t\t\tisDuringAutoFlip: false,\n\t\t\t};\n\t\t\tthis.activeFlips.set(leaf.index, flipState);\n\t\t}\n\n\t\t// Update delta\n\t\tflipState.delta = delta;\n\n\t\t// Calculate and apply flip position\n\t\tswitch (flipState.direction) {\n\t\t\tcase FlipDirection.Forward: {\n\t\t\t\tconst posForward = (delta / bookWidth) as FlipPosition;\n\t\t\t\tif (posForward > 1 || delta < 0) return;\n\t\t\t\tflipState.leaf.efficientFlipToPosition(posForward);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase FlipDirection.Backward: {\n\t\t\t\tconst posBackward = (1 - Math.abs(delta) / bookWidth) as FlipPosition;\n\t\t\t\tif (posBackward < 0 || delta > 0) return;\n\t\t\t\tflipState.leaf.efficientFlipToPosition(posBackward);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate onDragEnd(event: HammerInput) {\n\t\tconst flipState = this.currentManualFlip;\n\t\tif (!flipState) {\n\t\t\tthis.pendingFlipDirection = FlipDirection.None;\n\t\t\tthis.pendingFlipStartingPos = 0;\n\t\t\tthis.isDragging = false;\n\t\t\tthis.clearHoverShadow();\n\t\t\treturn;\n\t\t}\n\n\t\tconst ppsX = event.velocityX * 1000; // pixels per second\n\t\tlet flipTo: FlipPosition;\n\n\t\tswitch (flipState.direction) {\n\t\t\tcase FlipDirection.Forward:\n\t\t\t\tif (\n\t\t\t\t\t(this.isLTR ? ppsX < -this.fastDeltaThreshold : ppsX > this.fastDeltaThreshold) ||\n\t\t\t\t\tflipState.leaf.flipPosition >= 0.5\n\t\t\t\t) {\n\t\t\t\t\tflipTo = 1;\n\t\t\t\t} else {\n\t\t\t\t\tflipTo = 0;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase FlipDirection.Backward:\n\t\t\t\tif (\n\t\t\t\t\t(this.isLTR ? ppsX > this.fastDeltaThreshold : ppsX < -this.fastDeltaThreshold) ||\n\t\t\t\t\tflipState.leaf.flipPosition <= 0.5\n\t\t\t\t) {\n\t\t\t\t\tflipTo = 0;\n\t\t\t\t} else {\n\t\t\t\t\tflipTo = 1;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\treturn;\n\t\t}\n\n\t\t// Mark as auto-flip and reset pending state\n\t\tflipState.isDuringAutoFlip = true;\n\t\tthis.pendingFlipDirection = FlipDirection.None;\n\t\tthis.pendingFlipStartingPos = 0;\n\t\tthis.isDragging = false;\n\n\t\t// Complete the flip asynchronously - don't block new flips!\n\t\tflipState.leaf.flipToPosition(flipTo).then(() => {\n\t\t\tthis.activeFlips.delete(flipState.leaf.index);\n\t\t});\n\t}\n\n\tprivate handleTouchStart = (e: TouchEvent) => {\n\t\tif (e.touches.length > 1) {\n\t\t\treturn;\n\t\t}\n\t\tconst touch = e.touches[0];\n\t\tthis.touchStartingPos = { x: touch.pageX, y: touch.pageY };\n\t\tthis.touchStartedInNoFlipZone = this.isInsideNoFlipZone(e.target);\n\t};\n\n\tprivate handleTouchMove = (e: TouchEvent) => {\n\t\tif (e.touches.length > 1) {\n\t\t\treturn;\n\t\t}\n\t\t// Allow horizontal scroll (e.g. carousel) when touch started inside a no-flip zone\n\t\tif (this.touchStartedInNoFlipZone) {\n\t\t\treturn;\n\t\t}\n\t\tconst touch = e.touches[0];\n\t\tconst deltaX = touch.pageX - this.touchStartingPos.x;\n\t\tconst deltaY = touch.pageY - this.touchStartingPos.y;\n\t\t// only allow vertical scrolling, as if allowing horizontal scrolling, it will interfere with the flip gesture (for touch devices)\n\t\tif (Math.abs(deltaX) > Math.abs(deltaY)) {\n\t\t\te.preventDefault();\n\t\t}\n\t};\n\n\tprivate handleMouseMove = throttle(MOUSE_MOVE_THROTTLE_MS, (event: MouseEvent) => {\n\t\tif (!this.bookElement) return;\n\t\tif (this.isDragging || this.currentManualFlip || this.activeFlips.size > 0) {\n\t\t\tthis.clearHoverShadow();\n\t\t\treturn;\n\t\t}\n\n\t\tconst rect = this.bookElement.getBoundingClientRect();\n\t\tif (rect.width <= 0) return;\n\n\t\tconst x = event.clientX - rect.left;\n\t\tconst edgeZone = rect.width * EDGE_ZONE_RATIO;\n\t\tconst isLeftEdge = x <= edgeZone;\n\t\tconst isRightEdge = x >= rect.width - edgeZone;\n\n\t\tif (!isLeftEdge && !isRightEdge) {\n\t\t\tthis.clearHoverShadow();\n\t\t\treturn;\n\t\t}\n\n\t\tconst isForward = this.isLTR ? isRightEdge : isLeftEdge;\n\t\tconst direction = isForward ? FlipDirection.Forward : FlipDirection.Backward;\n\t\tconst leaf = this.getNextAvailableLeaf(direction);\n\t\tif (!leaf) {\n\t\t\tthis.clearHoverShadow();\n\t\t\treturn;\n\t\t}\n\n\t\tconst distanceFromEdge = isForward\n\t\t\t? this.isLTR\n\t\t\t\t? rect.width - x\n\t\t\t\t: x\n\t\t\t: this.isLTR\n\t\t\t\t? x\n\t\t\t\t: rect.width - x;\n\t\tconst edgeProgress = 1 - Math.min(1, distanceFromEdge / edgeZone);\n\t\tconst hoverStrength = edgeProgress * HOVER_STRENGTH_MAX;\n\n\t\tif (this.hoveredLeaf && this.hoveredLeaf !== leaf) {\n\t\t\tthis.hoveredLeaf.setHoverShadow(0);\n\t\t}\n\t\tthis.hoveredLeaf = leaf;\n\t\tleaf.setHoverShadow(hoverStrength);\n\t});\n\n\tprivate handleMouseLeave = () => {\n\t\tthis.clearHoverShadow();\n\t};\n\n\tprivate clearHoverShadow() {\n\t\tif (this.hoveredLeaf) {\n\t\t\tthis.hoveredLeaf.setHoverShadow(0);\n\t\t\tthis.hoveredLeaf = undefined;\n\t\t}\n\t}\n\tprivate onTurned(\n\t\tcurrentVisiblePageIndices: [number] | [number, number],\n\t\tprevVisibilePageIndices?: [number] | [number, number],\n\t) {\n\t\tfor (let i = 0; i < this.pageElements.length; i++) {\n\t\t\tconst pageElement = this.pageElements[i];\n\t\t\tconst inCurrent = currentVisiblePageIndices.includes(i);\n\t\t\tconst inPrev = prevVisibilePageIndices?.includes(i);\n\t\t\tconst action = inCurrent\n\t\t\t\t? pageElement.classList.add\n\t\t\t\t: !prevVisibilePageIndices || !inPrev\n\t\t\t\t\t? () => null\n\t\t\t\t\t: pageElement.classList.remove;\n\t\t\taction.bind(pageElement.classList)(\"current-page\");\n\t\t}\n\t\t// Update leaves buffer visibility after turn completes\n\t\tthis.updateLeavesBufferVisibility();\n\t\t// Notify React/consumer so content buffer can update (e.g. mount content for new visible range)\n\t\tif (this.onPageChanged) {\n\t\t\tthis.onPageChanged(this.currentPageIndex);\n\t\t}\n\t\t// TODO expose to outside using https://github.com/open-draft/strict-event-emitter, and just be a consumer internally.\n\t\t// TODO: set prev-page / next-page classes for prev/next pages as accordingally\n\t}\n\t/**\n\t * Get the index of the current (leftmost visible) page.\n\t * Returns 0 if no pages are visible.\n\t */\n\tget currentPageIndex(): number {\n\t\treturn this.prevVisiblePageIndices?.[0] ?? 0;\n\t}\n\n\t/**\n\t * Get the total number of pages in the flipbook.\n\t */\n\tget totalPages(): number {\n\t\treturn this.pagesCount;\n\t}\n\n\t/**\n\t * Check if the book is currently showing the first page(s).\n\t */\n\tget isFirstPage(): boolean {\n\t\treturn this.currentPageIndex === 0;\n\t}\n\n\t/**\n\t * Check if the book is currently showing the last page(s).\n\t */\n\tget isLastPage(): boolean {\n\t\tconst lastPageIndex = this.pagesCount - 1;\n\t\treturn this.prevVisiblePageIndices?.includes(lastPageIndex) ?? false;\n\t}\n\n\t/**\n\t * Check if there's an active auto-flip in the given direction.\n\t */\n\tprivate hasActiveAutoFlipInDirection(direction: FlipDirection): boolean {\n\t\tfor (const state of this.activeFlips.values()) {\n\t\t\tif (state.isDuringAutoFlip && state.direction === direction) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Animate flip to the next page.\n\t * @returns Promise that resolves when the flip animation completes\n\t */\n\tasync flipNext(): Promise<void> {\n\t\t// Block if there's an active auto-flip in the opposite direction\n\t\tif (this.hasActiveAutoFlipInDirection(FlipDirection.Backward)) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst leaf = this.getNextAvailableLeaf(FlipDirection.Forward);\n\t\tif (!leaf) return;\n\n\t\tconst flipState: FlipState = {\n\t\t\tleaf,\n\t\t\tdirection: FlipDirection.Forward,\n\t\t\tstartingPos: 0,\n\t\t\tdelta: 0,\n\t\t\tisDuringAutoFlip: true,\n\t\t};\n\t\tthis.activeFlips.set(leaf.index, flipState);\n\n\t\ttry {\n\t\t\tawait leaf.flipToPosition(1);\n\t\t} finally {\n\t\t\tthis.activeFlips.delete(leaf.index);\n\t\t}\n\t}\n\n\t/**\n\t * Animate flip to the previous page.\n\t * @returns Promise that resolves when the flip animation completes\n\t */\n\tasync flipPrev(): Promise<void> {\n\t\t// Block if there's an active auto-flip in the opposite direction\n\t\tif (this.hasActiveAutoFlipInDirection(FlipDirection.Forward)) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst leaf = this.getNextAvailableLeaf(FlipDirection.Backward);\n\t\tif (!leaf) return;\n\n\t\tconst flipState: FlipState = {\n\t\t\tleaf,\n\t\t\tdirection: FlipDirection.Backward,\n\t\t\tstartingPos: 0,\n\t\t\tdelta: 0,\n\t\t\tisDuringAutoFlip: true,\n\t\t};\n\t\tthis.activeFlips.set(leaf.index, flipState);\n\n\t\ttry {\n\t\t\tawait leaf.flipToPosition(0);\n\t\t} finally {\n\t\t\tthis.activeFlips.delete(leaf.index);\n\t\t}\n\t}\n\n\t/**\n\t * Animate to a specific page index.\n\t * Flips through pages sequentially to reach the target.\n\t * @param pageIndex - The target page index (0-based)\n\t * @returns Promise that resolves when all flip animations complete\n\t */\n\tasync goToPage(pageIndex: number): Promise<void> {\n\t\tif (pageIndex < 0 || pageIndex >= this.pagesCount) {\n\t\t\tconsole.warn(\n\t\t\t\t`goToPage: Invalid page index ${pageIndex}. Must be between 0 and ${this.pagesCount - 1}.`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst targetLeafIndex = Math.floor(pageIndex / 2);\n\t\tconst currentLeafIndex = Math.floor(this.currentPageIndex / 2);\n\n\t\tif (targetLeafIndex === currentLeafIndex) {\n\t\t\treturn; // Already at the target page\n\t\t}\n\n\t\tif (targetLeafIndex > currentLeafIndex) {\n\t\t\t// Flip forward\n\t\t\tfor (let i = currentLeafIndex; i < targetLeafIndex; i++) {\n\t\t\t\tawait this.flipNext();\n\t\t\t}\n\t\t} else {\n\t\t\t// Flip backward\n\t\t\tfor (let i = currentLeafIndex; i > targetLeafIndex; i--) {\n\t\t\t\tawait this.flipPrev();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Jump to a specific page instantly without animation.\n\t * @param pageIndex - The target page index (0-based)\n\t */\n\tjumpToPage(pageIndex: number): void {\n\t\tif (pageIndex < 0 || pageIndex >= this.pagesCount) {\n\t\t\tconsole.warn(\n\t\t\t\t`jumpToPage: Invalid page index ${pageIndex}. Must be between 0 and ${this.pagesCount - 1}.`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst targetLeafIndex = Math.floor(pageIndex / 2);\n\t\tconst isLastPage = pageIndex === this.pagesCount - 1;\n\t\tconst isOddPage = pageIndex % 2 === 1;\n\t\t// If targeting the last page and it's odd (back of leaf), close the book reversed\n\t\tconst closeBookReversed = isLastPage && isOddPage;\n\n\t\t// Set all leaves before target as turned, all after as not turned\n\t\tfor (let i = 0; i < this.leaves.length; i++) {\n\t\t\tconst leaf = this.leaves[i];\n\t\t\t// Turn leaves before target, or the target leaf itself if closing book reversed\n\t\t\tconst shouldBeTurned = i < targetLeafIndex || (closeBookReversed && i === targetLeafIndex);\n\n\t\t\tif (shouldBeTurned && !leaf.isTurned) {\n\t\t\t\t// Turn this leaf instantly\n\t\t\t\tleaf.flipPosition = 1;\n\t\t\t\tthis.applyLeafTransform(leaf, 1);\n\t\t\t} else if (!shouldBeTurned && leaf.isTurned) {\n\t\t\t\t// Unturn this leaf instantly\n\t\t\t\tleaf.flipPosition = 0;\n\t\t\t\tthis.applyLeafTransform(leaf, 0);\n\t\t\t}\n\t\t}\n\n\t\t// Update visible page indices\n\t\tif (closeBookReversed) {\n\t\t\t// Closed reversed: only show the last page\n\t\t\tthis.prevVisiblePageIndices = [pageIndex];\n\t\t} else {\n\t\t\tconst firstVisiblePageIndex = targetLeafIndex * 2;\n\t\t\tthis.prevVisiblePageIndices =\n\t\t\t\tfirstVisiblePageIndex + 1 < this.pagesCount\n\t\t\t\t\t? [firstVisiblePageIndex, firstVisiblePageIndex + 1]\n\t\t\t\t\t: [firstVisiblePageIndex];\n\t\t}\n\n\t\t// Update current-page classes\n\t\tfor (let i = 0; i < this.pageElements.length; i++) {\n\t\t\tconst pageElement = this.pageElements[i];\n\t\t\tif (this.prevVisiblePageIndices.includes(i)) {\n\t\t\t\tpageElement.classList.add(\"current-page\");\n\t\t\t} else {\n\t\t\t\tpageElement.classList.remove(\"current-page\");\n\t\t\t}\n\t\t}\n\n\t\t// Update leaves buffer visibility\n\t\tthis.updateLeavesBufferVisibility();\n\n\t\t// Notify callback\n\t\tif (this.onPageChanged) {\n\t\t\tthis.onPageChanged(this.currentPageIndex);\n\t\t}\n\t}\n\n\t/**\n\t * Apply transform styles to a leaf's pages for a given flip position.\n\t * Used for instant positioning (jumpToPage).\n\t */\n\tprivate applyLeafTransform(leaf: Leaf, position: number): void {\n\t\tleaf.applyTransform(position);\n\t}\n\n\t/**\n\t * Clean up event listeners and Hammer instance.\n\t * Should be called when the FlipBook is no longer needed.\n\t */\n\tdestroy() {\n\t\tif (this.hammer) {\n\t\t\tthis.hammer.destroy();\n\t\t\tthis.hammer = undefined;\n\t\t}\n\t\tif (this.bookElement) {\n\t\t\tthis.bookElement.removeEventListener(\"touchstart\", this.handleTouchStart as EventListener);\n\t\t\tthis.bookElement.removeEventListener(\"touchmove\", this.handleTouchMove as EventListener);\n\t\t\tthis.bookElement.removeEventListener(\n\t\t\t\t\"mousemove\",\n\t\t\t\tthis.handleMouseMove as unknown as EventListener,\n\t\t\t);\n\t\t\tthis.bookElement.removeEventListener(\"mouseleave\", this.handleMouseLeave as EventListener);\n\t\t}\n\t}\n}\n\nexport { FlipBook, type PageSemantics };\n"],"names":["FlipDirection","SHADOW_STRENGTH_MULTIPLIER","HIGHLIGHT_STRENGTH_MULTIPLIER","SHADOW_LIFT_PX","Leaf","index","pages","isFlipped","bookProperties","onTurned","value","progress","clamped","flipPosition","velocity","resolve","currentFlipPosition","duration","start","step","timestamp","elapsed","newPosition","throttle","position","clampedPosition","shadowFromFlip","shadowProgress","shadowStrength","highlightStrength","lift","page","isLTR","isOdd","rotateY","translateX","scaleX","origin","edge","AspectRatioImpl","width","height","aspectRatio","Size","rhsAspectRatio","rhsAspectRatioValue","DEFAULT_FAST_DELTA","EDGE_ZONE_RATIO","HOVER_STRENGTH_MAX","MOUSE_MOVE_THROTTLE_MS","FlipBook","el","secondLeafIndex","i","leaf","options","selector","debug","bookElement","pageElements","leavesCount","coverSize","leafSize","pageElement","pageIndex","leafIndex","isOddPage","isInitiallyTurned","firstVisibleLeafIndex","isCurrentPage","transform","direction","currentVisiblePageIndices","v","prevVisiblePageIndices","firstVisiblePageIndex","lastLeafFirstPage","Hammer","leftLeaf","rightLeaf","currentLeafIndex","bufferStart","bufferEnd","isWithinBuffer","debugBar","activeFlipsInfo","idx","state","event","flipState","currentPos","bookWidth","delta","posForward","posBackward","ppsX","flipTo","e","touch","deltaX","deltaY","rect","x","edgeZone","isLeftEdge","isRightEdge","isForward","distanceFromEdge","hoverStrength","prevVisibilePageIndices","inCurrent","inPrev","lastPageIndex","targetLeafIndex","isLastPage","closeBookReversed","shouldBeTurned"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"flipbook.js","sources":["../src/flip-direction.ts","../src/leaf.ts","../src/aspect-ratio.ts","../src/size.ts","../src/flipbook.ts"],"sourcesContent":["export enum FlipDirection {\n\tForward = \"Forward\",\n\tBackward = \"Backward\",\n\tNone = \"None\",\n}\n","import { throttle } from \"throttle-debounce\";\nimport type { IntRange } from \"type-fest\";\nimport { FlipDirection } from \"./flip-direction\";\n\n/** Multiplier for shadow intensity (slightly stronger than base) */\nconst SHADOW_STRENGTH_MULTIPLIER = 1.1;\n/** Multiplier for highlight intensity (slightly weaker than base) */\nconst HIGHLIGHT_STRENGTH_MULTIPLIER = 0.9;\n/** Maximum lift in pixels for the shadow effect */\nconst SHADOW_LIFT_PX = 8;\n\n// number between 1 to infinity\nexport type DegreesPerSecond = IntRange<1, typeof Infinity>;\nexport type FlipPosition = IntRange<0, 2>;\nexport const FLIPPED = true;\nexport const NOT_FLIPPED = false;\nexport class Leaf {\n\tprivate currentAnimation: Promise<void> | null = null;\n\tprivate targetFlipPosition: FlipPosition | null = null;\n\tprivate wrappedFlipPosition: number;\n\tprivate hoverShadow = 0;\n\n\tconstructor(\n\t\treadonly index: number,\n\t\treadonly pages: [HTMLElement, HTMLElement | undefined],\n\t\tisFlipped: boolean,\n\t\tprivate readonly bookProperties: {\n\t\t\tisLTR: boolean;\n\t\t\tpagesCount: number;\n\t\t\tleavesCount: number;\n\t\t},\n\t\tprivate readonly onTurned: (direction: FlipDirection) => void,\n\t) {\n\t\tthis.wrappedFlipPosition = isFlipped ? 1 : 0;\n\t\t// TODO: rethink this\n\t\t// if(isFlipped) {\n\t\t// // this.flipToPosition(1);\n\t\t// }\n\t}\n\n\tget isTurned(): boolean {\n\t\treturn this.flipPosition === 1;\n\t}\n\tget isTurning(): boolean {\n\t\treturn this.flipPosition !== 0;\n\t}\n\tget isCover(): boolean {\n\t\treturn this.isFirst || this.isLast;\n\t}\n\tget isFirst(): boolean {\n\t\treturn this.index === 0;\n\t}\n\tget isLast(): boolean {\n\t\treturn this.index === this.bookProperties.leavesCount - 1;\n\t}\n\tset flipPosition(value: number) {\n\t\tthis.wrappedFlipPosition = Math.max(0, Math.min(1, value)) as FlipPosition;\n\t}\n\tget flipPosition(): number {\n\t\treturn this.wrappedFlipPosition;\n\t}\n\n\tsetHoverShadow(progress: number): void {\n\t\tconst clamped = Math.max(0, Math.min(1, progress));\n\t\tif (this.hoverShadow === clamped) return;\n\t\tthis.hoverShadow = clamped;\n\t\tthis.applyTransform(this.flipPosition);\n\t}\n\n\tasync flipToPosition(\n\t\tflipPosition: FlipPosition,\n\t\tvelocity: DegreesPerSecond = 225 as DegreesPerSecond,\n\t) {\n\t\tif (this.currentAnimation) {\n\t\t\tawait this.currentAnimation;\n\t\t}\n\n\t\tif (this.flipPosition === flipPosition) {\n\t\t\treturn Promise.resolve();\n\t\t}\n\n\t\tif (this.targetFlipPosition === flipPosition) {\n\t\t\treturn this.currentAnimation ?? Promise.resolve();\n\t\t}\n\n\t\tthis.targetFlipPosition = flipPosition;\n\n\t\tthis.currentAnimation = new Promise<void>((resolve) => {\n\t\t\tconst currentFlipPosition = this.flipPosition;\n\t\t\tconst distance = Math.abs(flipPosition - currentFlipPosition);\n\t\t\tconst duration = ((distance * 180) / velocity) * 1000; // duration in milliseconds\n\n\t\t\tconst start = performance.now();\n\t\t\tconst step = (timestamp: number) => {\n\t\t\t\tconst elapsed = timestamp - start;\n\n\t\t\t\tif (elapsed < 0) {\n\t\t\t\t\trequestAnimationFrame(step);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst progress = Math.min(elapsed / duration, 1);\n\t\t\t\tconst newPosition = currentFlipPosition + progress * (flipPosition - currentFlipPosition);\n\n\t\t\t\tthis.applyTransform(newPosition);\n\n\t\t\t\t// Ensure the new position is within valid bounds [0, 1]\n\t\t\t\tthis.flipPosition = Math.max(0, Math.min(1, newPosition)) as FlipPosition;\n\t\t\t\tif (this.flipPosition === 1 || this.flipPosition === 0) {\n\t\t\t\t\tthis.onTurned(this.flipPosition === 1 ? FlipDirection.Forward : FlipDirection.Backward);\n\t\t\t\t}\n\t\t\t\t// Detailed log for debugging\n\t\t\t\t// console.log(\n\t\t\t\t// `Timestamp: ${timestamp}, Elapsed: ${elapsed}, Progress: ${progress}, Current Position: ${currentFlipPosition}, Requested Position: ${flipPosition}, New Position: ${this.flipPosition}`\n\t\t\t\t// );\n\n\t\t\t\tif (progress < 1) {\n\t\t\t\t\trequestAnimationFrame(step);\n\t\t\t\t} else {\n\t\t\t\t\tthis.currentAnimation = null;\n\t\t\t\t\tthis.targetFlipPosition = null;\n\t\t\t\t\tresolve();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\trequestAnimationFrame(step);\n\t\t});\n\n\t\treturn this.currentAnimation;\n\t}\n\n\tasync efficientFlipToPosition(\n\t\tflipPosition: FlipPosition,\n\t\tvelocity: DegreesPerSecond = 20000 as DegreesPerSecond,\n\t) {\n\t\treturn throttle(1, this.flipToPosition.bind(this))(flipPosition, velocity);\n\t}\n\n\tapplyTransform(position: number): void {\n\t\tconst clampedPosition = Math.max(0, Math.min(1, position));\n\t\tconst shadowFromFlip = Math.sin(clampedPosition * Math.PI);\n\t\tconst shadowProgress = Math.max(shadowFromFlip, this.hoverShadow);\n\t\tconst shadowStrength = Math.min(1, shadowProgress * SHADOW_STRENGTH_MULTIPLIER);\n\t\tconst highlightStrength = Math.min(1, shadowProgress * HIGHLIGHT_STRENGTH_MULTIPLIER);\n\t\tconst lift = `${(shadowProgress * SHADOW_LIFT_PX).toFixed(3)}px`;\n\n\t\tthis.pages.forEach((page, index) => {\n\t\t\tconst isLTR = this.bookProperties.isLTR;\n\t\t\tif (!page) return;\n\n\t\t\tconst isOdd = (index % 2) + 1 === 1;\n\t\t\tconst degrees = isOdd\n\t\t\t\t? isLTR\n\t\t\t\t\t? clampedPosition > 0.5\n\t\t\t\t\t\t? 180 - clampedPosition * 180\n\t\t\t\t\t\t: -clampedPosition * 180\n\t\t\t\t\t: clampedPosition > 0.5\n\t\t\t\t\t\t? -(180 - clampedPosition * 180)\n\t\t\t\t\t\t: clampedPosition * 180\n\t\t\t\t: isLTR\n\t\t\t\t\t? clampedPosition < 0.5\n\t\t\t\t\t\t? -clampedPosition * 180\n\t\t\t\t\t\t: 180 - clampedPosition * 180\n\t\t\t\t\t: clampedPosition < 0.5\n\t\t\t\t\t\t? clampedPosition * 180\n\t\t\t\t\t\t: -(180 - clampedPosition * 180);\n\t\t\tconst rotateY = `${degrees}deg`;\n\t\t\tconst translateX = `${isOdd ? (isLTR ? \"100%\" : \"-100%\") : \"0px\"}`;\n\t\t\tconst scaleX = isOdd ? (clampedPosition > 0.5 ? -1 : 1) : clampedPosition < 0.5 ? -1 : 1;\n\t\t\tconst origin = isOdd ? (isLTR ? \"left\" : \"right\") : isLTR ? \"right\" : \"left\";\n\t\t\tconst edge = origin === \"left\" ? \"right\" : \"left\";\n\n\t\t\tpage.style.transform = `translateX(${translateX})rotateY(${rotateY})scaleX(${scaleX})`;\n\t\t\tpage.style.transformOrigin = origin;\n\t\t\tpage.style.zIndex = `${\n\t\t\t\tclampedPosition > 0.5\n\t\t\t\t\t? page.dataset.pageIndex\n\t\t\t\t\t: this.bookProperties.pagesCount - (page.dataset.pageIndex as unknown as number)\n\t\t\t}`;\n\t\t\tpage.style.setProperty(\"--inner-shadow-shadow\", shadowStrength.toFixed(3));\n\t\t\tpage.style.setProperty(\"--inner-shadow-highlight\", highlightStrength.toFixed(3));\n\t\t\tpage.style.setProperty(\"--inner-shadow-lift\", lift);\n\t\t\tpage.style.setProperty(\"--inner-shadow-edge\", edge);\n\t\t});\n\t}\n}\n","export interface AspectRatio {\n\twidth: number;\n\theight: number;\n}\nexport class AspectRatioImpl implements AspectRatio {\n\tstatic from(aspectRatio: AspectRatio) {\n\t\treturn new AspectRatioImpl(aspectRatio.width, aspectRatio.height);\n\t}\n\tconstructor(\n\t\tpublic readonly width: number,\n\t\tpublic readonly height: number,\n\t) {}\n\tget value(): number {\n\t\treturn this.width / this.height;\n\t}\n}\n","import { type AspectRatio, AspectRatioImpl } from \"./aspect-ratio\";\n\nexport class Size {\n\taspectRatio: AspectRatioImpl;\n\taspectRatioFit(rhsAspectRatio: AspectRatio) {\n\t\tconst rhsAspectRatioValue = AspectRatioImpl.from(rhsAspectRatio).value;\n\t\treturn this.aspectRatio.value > rhsAspectRatioValue\n\t\t\t? new Size(this.height * rhsAspectRatioValue, this.height)\n\t\t\t: new Size(this.width, this.width / rhsAspectRatioValue);\n\t}\n\tconstructor(\n\t\tpublic readonly width: number,\n\t\tpublic readonly height: number,\n\t) {\n\t\tthis.aspectRatio = new AspectRatioImpl(width, height);\n\t}\n\tget asString(): string {\n\t\treturn `${this.width}x${this.height}`;\n\t}\n}\n","import \"./pages.scss\";\nimport \"./flipbook.scss\";\nimport Hammer from \"hammerjs\";\nimport { throttle } from \"throttle-debounce\";\nimport type { AspectRatio } from \"./aspect-ratio\";\nimport type { FlipBookOptions } from \"./flip-book-options\";\nimport { FlipDirection } from \"./flip-direction\";\nimport { type FlipPosition, Leaf } from \"./leaf\";\nimport type { PageSemantics } from \"./page-semantics\";\nimport { Size } from \"./size\";\n\n/** Default threshold for fast flip detection (in ms) */\nconst DEFAULT_FAST_DELTA = 500;\n\n/** Percentage of book width that triggers hover shadow effect */\nconst EDGE_ZONE_RATIO = 0.18;\n/** Maximum hover shadow strength (0-1) */\nconst HOVER_STRENGTH_MAX = 0.12;\n/** Throttle interval for mouse move handler in ms */\nconst MOUSE_MOVE_THROTTLE_MS = 16;\n\n/** State for a single flip operation - enables concurrent page flipping */\ninterface FlipState {\n\tleaf: Leaf;\n\tdirection: FlipDirection;\n\tstartingPos: number;\n\tdelta: number;\n\tisDuringAutoFlip: boolean;\n}\n\n/**\n * Core FlipBook class for creating realistic page-flip animations.\n *\n * @example\n * ```typescript\n * import { FlipBook } from 'html-flip-book-vanilla';\n *\n * const flipBook = new FlipBook({\n * pagesCount: 10,\n * direction: 'ltr',\n * });\n *\n * flipBook.render('#book-container');\n *\n * // Navigate programmatically\n * await flipBook.flipNext();\n * await flipBook.goToPage(5);\n * ```\n */\nclass FlipBook {\n\tbookElement?: HTMLElement;\n\tprivate pageElements: HTMLElement[] = [];\n\tprivate readonly pagesCount: number;\n\tprivate readonly leafAspectRatio: AspectRatio = { width: 2, height: 3 };\n\tprivate readonly coverAspectRatio: AspectRatio = {\n\t\twidth: 2.15,\n\t\theight: 3.15,\n\t};\n\tprivate readonly direction: \"rtl\" | \"ltr\" = \"ltr\";\n\tprivate readonly fastDeltaThreshold: number = DEFAULT_FAST_DELTA;\n\tprivate readonly initialTurnedLeaves: Set<number> = new Set();\n\tprivate readonly onPageChanged?: (pageIndex: number) => void;\n\tprivate readonly pageSemantics: PageSemantics | undefined;\n\tprivate readonly leavesBuffer?: number;\n\tprivate leaves: Leaf[] = [];\n\t// flipping state - supports concurrent page flipping\n\tprivate activeFlips: Map<number, FlipState> = new Map();\n\tprivate pendingFlipStartingPos = 0;\n\tprivate pendingFlipDirection: FlipDirection = FlipDirection.None;\n\tprivate isDragging = false;\n\tprivate hoveredLeaf: Leaf | undefined;\n\ttouchStartingPos = { x: 0, y: 0 };\n\t/** True when the current touch started inside a [data-flipbook-no-flip] element (e.g. carousel). */\n\tprivate touchStartedInNoFlipZone = false;\n\tprivate prevVisiblePageIndices: [number] | [number, number] | undefined;\n\t// Hammer instance for cleanup\n\tprivate hammer: HammerManager | undefined;\n\n\tprivate static readonly NO_FLIP_SELECTOR = \"[data-flipbook-no-flip]\";\n\n\tprivate isInsideNoFlipZone(el: EventTarget | null): boolean {\n\t\treturn el instanceof Element && el.closest(FlipBook.NO_FLIP_SELECTOR) != null;\n\t}\n\tprivate get isLTR(): boolean {\n\t\treturn this.direction === \"ltr\";\n\t}\n\n\tprivate get currentOrTurningLeaves(): [Leaf | undefined, Leaf | undefined] {\n\t\tlet secondLeafIndex = -1;\n\t\tfor (let i = this.leaves.length - 1; i >= 0; i--) {\n\t\t\tconst leaf = this.leaves[i];\n\t\t\tif (leaf.isTurned || leaf.isTurning) {\n\t\t\t\tsecondLeafIndex = leaf.index + 1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn secondLeafIndex === -1\n\t\t\t? [undefined, this.leaves[0]]\n\t\t\t: secondLeafIndex === this.leaves.length\n\t\t\t\t? [this.leaves[secondLeafIndex - 1], undefined]\n\t\t\t\t: [this.leaves[secondLeafIndex - 1], this.leaves[secondLeafIndex]];\n\t}\n\n\tconstructor(options: FlipBookOptions) {\n\t\tthis.pagesCount = options.pagesCount;\n\t\tthis.leafAspectRatio = options.leafAspectRatio || this.leafAspectRatio;\n\t\tthis.coverAspectRatio = options.coverAspectRatio || this.coverAspectRatio;\n\t\tthis.direction = options.direction || this.direction;\n\t\tthis.fastDeltaThreshold = options.fastDeltaThreshold ?? this.fastDeltaThreshold;\n\t\tthis.initialTurnedLeaves = new Set(options.initialTurnedLeaves ?? []);\n\t\tthis.pageSemantics = options.pageSemantics;\n\t\tthis.onPageChanged = options.onPageChanged;\n\t\tthis.leavesBuffer = options.leavesBuffer;\n\t}\n\n\trender(selector: string, debug = false) {\n\t\tconst bookElement = document.querySelector(selector);\n\t\tif (!bookElement) {\n\t\t\tthrow new Error(`Couldn't find container with selector: ${selector}`);\n\t\t}\n\t\tthis.bookElement = bookElement as HTMLElement;\n\t\tif (!this.bookElement.classList.contains(\"flipbook\")) {\n\t\t\tthis.bookElement.classList.add(\"flipbook\");\n\t\t}\n\n\t\tconst pageElements = bookElement.querySelectorAll(\".page\");\n\t\tif (!pageElements.length) {\n\t\t\tthrow new Error(\"No pages found in flipbook\");\n\t\t}\n\t\tthis.pageElements = Array.from(pageElements) as HTMLElement[];\n\t\tthis.leaves.splice(0, this.leaves.length);\n\t\tconst leavesCount = Math.ceil(this.pagesCount / 2);\n\t\tconst maxCoverSize = new Size(this.bookElement.clientWidth / 2, this.bookElement.clientHeight);\n\t\tconst coverSize = maxCoverSize.aspectRatioFit(this.coverAspectRatio);\n\t\tconst leafSize = new Size(\n\t\t\t(coverSize.width * this.leafAspectRatio.width) / this.coverAspectRatio.width,\n\t\t\t(coverSize.height * this.leafAspectRatio.height) / this.coverAspectRatio.height,\n\t\t);\n\t\tthis.bookElement.style.perspective = `${Math.min(leafSize.width * 2, leafSize.height) * 2}px`;\n\t\tthis.pageElements.forEach((pageElement, pageIndex) => {\n\t\t\tpageElement.style.width = `${leafSize.width}px`;\n\t\t\tpageElement.style.height = `${leafSize.height}px`;\n\n\t\t\tpageElement.style.zIndex = `${this.pagesCount - pageIndex}`;\n\t\t\tpageElement.dataset.pageIndex = pageIndex.toString();\n\t\t\tpageElement.style[this.isLTR ? \"left\" : \"right\"] =\n\t\t\t\t`${(bookElement.clientWidth - 2 * leafSize.width) / 2}px`;\n\t\t\tpageElement.style.top = `${(bookElement.clientHeight - leafSize.height) / 2}px`;\n\t\t\tpageElement.dataset.pageSemanticName =\n\t\t\t\tthis.pageSemantics?.indexToSemanticName(pageIndex) ?? \"\";\n\t\t\tpageElement.dataset.pageTitle = this.pageSemantics?.indexToTitle(pageIndex) ?? \"\";\n\n\t\t\tconst leafIndex = Math.floor(pageIndex / 2);\n\t\t\tconst isOddPage = (pageIndex + 1) % 2 === 1;\n\t\t\tconst isInitiallyTurned = this.initialTurnedLeaves.has(leafIndex);\n\n\t\t\t// Determine current page based on initial turned leaves\n\t\t\t// Current pages are the first two visible pages after all initially turned leaves\n\t\t\tconst firstVisibleLeafIndex =\n\t\t\t\tthis.initialTurnedLeaves.size > 0 ? Math.max(...this.initialTurnedLeaves) + 1 : 0;\n\t\t\tconst isCurrentPage =\n\t\t\t\tleafIndex === firstVisibleLeafIndex ||\n\t\t\t\t(leafIndex === firstVisibleLeafIndex - 1 && isInitiallyTurned);\n\n\t\t\tpageElement.classList.add(\n\t\t\t\tisOddPage ? \"odd\" : \"even\",\n\t\t\t\t...(isCurrentPage ? [\"current-page\"] : []),\n\t\t\t);\n\t\t\tif (isOddPage) {\n\t\t\t\t// Apply correct initial transform based on turned state\n\t\t\t\tif (isInitiallyTurned) {\n\t\t\t\t\t// Fully turned: rotateY(180) or similar based on direction\n\t\t\t\t\tconst scaleX = -1;\n\t\t\t\t\tconst transform = `translateX(${this.isLTR ? \"100%\" : \"-100%\"})rotateY(${this.isLTR ? \"180deg\" : \"-180deg\"})scaleX(${scaleX})`;\n\t\t\t\t\tpageElement.style.transform = transform;\n\t\t\t\t\tpageElement.style.transformOrigin = this.isLTR ? \"left\" : \"right\";\n\t\t\t\t\tpageElement.style.zIndex = `${pageIndex}`; // Turned pages have lower z-index\n\t\t\t\t} else {\n\t\t\t\t\tpageElement.style.transform = `translateX(${this.isLTR ? `` : `-`}100%)`;\n\t\t\t\t\tpageElement.style.zIndex = `${this.pagesCount - pageIndex}`;\n\t\t\t\t}\n\n\t\t\t\tthis.leaves[leafIndex] = new Leaf(\n\t\t\t\t\tleafIndex,\n\t\t\t\t\t[pageElement, undefined],\n\t\t\t\t\tisInitiallyTurned,\n\t\t\t\t\t{\n\t\t\t\t\t\tisLTR: this.isLTR,\n\t\t\t\t\t\tleavesCount: leavesCount,\n\t\t\t\t\t\tpagesCount: this.pagesCount,\n\t\t\t\t\t},\n\t\t\t\t\t(direction: FlipDirection) => {\n\t\t\t\t\t\tconst currentVisiblePageIndices: [number] | [number, number] =\n\t\t\t\t\t\t\tdirection === FlipDirection.Forward\n\t\t\t\t\t\t\t\t? pageIndex + 2 === this.pagesCount\n\t\t\t\t\t\t\t\t\t? [pageIndex + 1]\n\t\t\t\t\t\t\t\t\t: [pageIndex + 1, pageIndex + 2]\n\t\t\t\t\t\t\t\t: pageIndex === 0\n\t\t\t\t\t\t\t\t\t? [pageIndex]\n\t\t\t\t\t\t\t\t\t: [pageIndex - 1, pageIndex];\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tthis.prevVisiblePageIndices &&\n\t\t\t\t\t\t\tthis.prevVisiblePageIndices.length === currentVisiblePageIndices.length &&\n\t\t\t\t\t\t\tcurrentVisiblePageIndices.every((v, i) => v === this.prevVisiblePageIndices?.[i])\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst prevVisiblePageIndices = this.prevVisiblePageIndices;\n\t\t\t\t\t\tthis.prevVisiblePageIndices = currentVisiblePageIndices;\n\n\t\t\t\t\t\t// TODO expose to outside using https://github.com/open-draft/strict-event-emitter, and just be a consumer internally\n\t\t\t\t\t\tthis.onTurned(currentVisiblePageIndices, prevVisiblePageIndices);\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t// Even page (back side of leaf)\n\t\t\t\tif (isInitiallyTurned) {\n\t\t\t\t\t// Fully turned: apply matching transform for back side\n\t\t\t\t\tconst scaleX = 1;\n\t\t\t\t\tpageElement.style.transform = `translateX(0px)rotateY(${this.isLTR ? \"180deg\" : \"-180deg\"})scaleX(${scaleX})`;\n\t\t\t\t\tpageElement.style.transformOrigin = this.isLTR ? \"right\" : \"left\";\n\t\t\t\t\tpageElement.style.zIndex = `${pageIndex}`; // Turned pages have lower z-index\n\t\t\t\t} else {\n\t\t\t\t\tpageElement.style.transform = `scaleX(-1)translateX(${this.isLTR ? `-` : ``}100%)`;\n\t\t\t\t\tpageElement.style.zIndex = `${this.pagesCount - pageIndex}`;\n\t\t\t\t}\n\t\t\t\tthis.leaves[leafIndex].pages[1] = pageElement;\n\t\t\t}\n\t\t});\n\n\t\t// Set initial visible page indices based on initially turned leaves\n\t\tconst firstVisibleLeafIndex =\n\t\t\tthis.initialTurnedLeaves.size > 0 ? Math.max(...this.initialTurnedLeaves) + 1 : 0;\n\t\tconst firstVisiblePageIndex = firstVisibleLeafIndex * 2;\n\n\t\t// Handle edge case where all leaves are turned (showing last page only)\n\t\tif (firstVisiblePageIndex >= this.pagesCount) {\n\t\t\t// All leaves are turned - show the last page(s)\n\t\t\tconst lastLeafIndex = Math.ceil(this.pagesCount / 2) - 1;\n\t\t\tconst lastLeafFirstPage = lastLeafIndex * 2;\n\t\t\tthis.prevVisiblePageIndices =\n\t\t\t\tlastLeafFirstPage + 1 < this.pagesCount\n\t\t\t\t\t? [lastLeafFirstPage, lastLeafFirstPage + 1]\n\t\t\t\t\t: [lastLeafFirstPage];\n\t\t} else {\n\t\t\tthis.prevVisiblePageIndices =\n\t\t\t\tfirstVisiblePageIndex + 1 < this.pagesCount\n\t\t\t\t\t? [firstVisiblePageIndex, firstVisiblePageIndex + 1]\n\t\t\t\t\t: [firstVisiblePageIndex];\n\t\t}\n\n\t\tthis.hammer = new Hammer(this.bookElement);\n\t\tthis.hammer.on(\"panstart\", this.onDragStart.bind(this));\n\t\tthis.hammer.on(\"panmove\", this.onDragUpdate.bind(this));\n\t\tthis.hammer.on(\"panend\", this.onDragEnd.bind(this));\n\t\tthis.bookElement.addEventListener(\"touchstart\", this.handleTouchStart.bind(this), {\n\t\t\tpassive: false,\n\t\t});\n\t\tthis.bookElement.addEventListener(\"touchmove\", this.handleTouchMove.bind(this), {\n\t\t\tpassive: false,\n\t\t});\n\t\tthis.bookElement.addEventListener(\n\t\t\t\"mousemove\",\n\t\t\tthis.handleMouseMove as unknown as EventListener,\n\t\t);\n\t\tthis.bookElement.addEventListener(\"mouseleave\", this.handleMouseLeave as EventListener);\n\t\t// Apply initial leaves buffer visibility\n\t\tthis.updateLeavesBufferVisibility();\n\t\tif (debug) this.fillDebugBar();\n\t}\n\n\t/**\n\t * Update visibility of leaves based on the buffer setting.\n\t * Leaves outside the buffer range are hidden for performance.\n\t */\n\tprivate updateLeavesBufferVisibility(): void {\n\t\tif (this.leavesBuffer === undefined) {\n\t\t\treturn; // No buffer - all leaves stay visible\n\t\t}\n\n\t\tconst leavesCount = this.leaves.length;\n\t\tif (leavesCount === 0) return;\n\n\t\t// Find the current leaf index (the first non-turned leaf, or last leaf if all turned)\n\t\tconst [leftLeaf, rightLeaf] = this.currentOrTurningLeaves;\n\t\tconst currentLeafIndex = rightLeaf?.index ?? leftLeaf?.index ?? 0;\n\n\t\t// Calculate buffer range\n\t\tconst bufferStart = Math.max(0, currentLeafIndex - this.leavesBuffer);\n\t\tconst bufferEnd = Math.min(leavesCount - 1, currentLeafIndex + this.leavesBuffer);\n\n\t\t// Update visibility of all leaves\n\t\tfor (let i = 0; i < leavesCount; i++) {\n\t\t\tconst leaf = this.leaves[i];\n\t\t\tconst isWithinBuffer = i >= bufferStart && i <= bufferEnd;\n\n\t\t\t// Update both pages of the leaf\n\t\t\tfor (const page of leaf.pages) {\n\t\t\t\tif (page) {\n\t\t\t\t\tpage.style.display = isWithinBuffer ? \"\" : \"none\";\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate fillDebugBar() {\n\t\tconst debugBar = document.createElement(\"div\");\n\t\tdebugBar.className = \"flipbook-debug-bar\";\n\t\tthis.bookElement?.appendChild(debugBar);\n\t\tsetInterval(() => {\n\t\t\t// Populate debug bar with relevant information\n\t\t\tconst activeFlipsInfo = Array.from(this.activeFlips.entries())\n\t\t\t\t.map(([idx, state]) => `${idx}:${state.leaf.flipPosition.toFixed(2)}`)\n\t\t\t\t.join(\", \");\n\t\t\tdebugBar.innerHTML = `\n <div>Direction: ${this.isLTR ? \"LTR\" : \"RTL\"}</div>\n <div>Active Flips: ${this.activeFlips.size} [${activeFlipsInfo}]</div>\n <div>Pending Flip dir: ${this.pendingFlipDirection}</div>\n `;\n\t\t}, 10);\n\t}\n\n\t/**\n\t * Get the current manually-controlled flip state (the one being dragged).\n\t * Returns undefined if no manual flip is in progress.\n\t */\n\tprivate get currentManualFlip(): FlipState | undefined {\n\t\tfor (const state of this.activeFlips.values()) {\n\t\t\tif (!state.isDuringAutoFlip) {\n\t\t\t\treturn state;\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Find the next available leaf for flipping in the given direction.\n\t * Excludes leaves that are already being flipped.\n\t */\n\tprivate getNextAvailableLeaf(direction: FlipDirection): Leaf | undefined {\n\t\tif (direction === FlipDirection.Forward) {\n\t\t\t// Find the rightmost leaf that's not already flipping\n\t\t\t// Start from the current right leaf and go forward\n\t\t\tconst [, rightLeaf] = this.currentOrTurningLeaves;\n\t\t\tif (!rightLeaf) return undefined;\n\n\t\t\t// Find a leaf that's not in activeFlips\n\t\t\tfor (let i = rightLeaf.index; i < this.leaves.length; i++) {\n\t\t\t\tconst leaf = this.leaves[i];\n\t\t\t\tif (!this.activeFlips.has(leaf.index) && !leaf.isTurned) {\n\t\t\t\t\treturn leaf;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn undefined;\n\t\t} else if (direction === FlipDirection.Backward) {\n\t\t\t// Find the leftmost leaf that's not already flipping\n\t\t\tconst [leftLeaf] = this.currentOrTurningLeaves;\n\t\t\tif (!leftLeaf) return undefined;\n\n\t\t\t// Find a leaf that's not in activeFlips\n\t\t\tfor (let i = leftLeaf.index; i >= 0; i--) {\n\t\t\t\tconst leaf = this.leaves[i];\n\t\t\t\tif (!this.activeFlips.has(leaf.index) && (leaf.isTurned || leaf.isTurning)) {\n\t\t\t\t\treturn leaf;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tprivate onDragStart(event: HammerInput) {\n\t\t// Do not start flip when gesture begins inside a no-flip zone (e.g. horizontal carousel)\n\t\tif (this.isInsideNoFlipZone(event.srcEvent?.target ?? null)) {\n\t\t\treturn;\n\t\t}\n\t\t// Allow starting a new flip even if others are in auto-flip mode\n\t\t// Only block if we already have a manual flip in progress\n\t\tif (this.currentManualFlip) {\n\t\t\tthis.pendingFlipDirection = FlipDirection.None;\n\t\t\tthis.pendingFlipStartingPos = 0;\n\t\t\treturn;\n\t\t}\n\t\tthis.isDragging = true;\n\t\tthis.clearHoverShadow();\n\t\tthis.pendingFlipStartingPos = event.center.x;\n\t\tthis.pendingFlipDirection = FlipDirection.None;\n\t}\n\n\tprivate onDragUpdate(event: HammerInput) {\n\t\t// Get or create the current manual flip state\n\t\tlet flipState = this.currentManualFlip;\n\n\t\tconst currentPos = event.center.x;\n\t\tconst bookWidth = this.bookElement?.clientWidth ?? 0;\n\n\t\t// Calculate delta\n\t\tconst delta = this.isLTR\n\t\t\t? this.pendingFlipStartingPos - currentPos\n\t\t\t: currentPos - this.pendingFlipStartingPos;\n\n\t\tif (Math.abs(delta) > bookWidth) return;\n\t\tif (delta === 0) return;\n\n\t\t// Determine direction on first meaningful movement\n\t\tconst direction =\n\t\t\tthis.pendingFlipDirection !== FlipDirection.None\n\t\t\t\t? this.pendingFlipDirection\n\t\t\t\t: delta > 0\n\t\t\t\t\t? FlipDirection.Forward\n\t\t\t\t\t: FlipDirection.Backward;\n\n\t\t// Lock in direction for this flip\n\t\tif (this.pendingFlipDirection === FlipDirection.None) {\n\t\t\tthis.pendingFlipDirection = direction;\n\t\t}\n\n\t\t// Block starting a new flip in opposite direction while auto-flip is in progress\n\t\t// This prevents visual glitches when swiping backward while a forward flip is animating\n\t\tif (!flipState) {\n\t\t\tfor (const state of this.activeFlips.values()) {\n\t\t\t\tif (state.isDuringAutoFlip && state.direction !== direction) {\n\t\t\t\t\t// There's an auto-flip in the opposite direction - block this new flip\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// If we don't have a flip state yet, create one\n\t\tif (!flipState) {\n\t\t\tconst leaf = this.getNextAvailableLeaf(direction);\n\t\t\tif (!leaf) return;\n\n\t\t\tflipState = {\n\t\t\t\tleaf,\n\t\t\t\tdirection,\n\t\t\t\tstartingPos: this.pendingFlipStartingPos,\n\t\t\t\tdelta: 0,\n\t\t\t\tisDuringAutoFlip: false,\n\t\t\t};\n\t\t\tthis.activeFlips.set(leaf.index, flipState);\n\t\t}\n\n\t\t// Update delta\n\t\tflipState.delta = delta;\n\n\t\t// Calculate and apply flip position\n\t\tswitch (flipState.direction) {\n\t\t\tcase FlipDirection.Forward: {\n\t\t\t\tconst posForward = (delta / bookWidth) as FlipPosition;\n\t\t\t\tif (posForward > 1 || delta < 0) return;\n\t\t\t\tflipState.leaf.efficientFlipToPosition(posForward);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase FlipDirection.Backward: {\n\t\t\t\tconst posBackward = (1 - Math.abs(delta) / bookWidth) as FlipPosition;\n\t\t\t\tif (posBackward < 0 || delta > 0) return;\n\t\t\t\tflipState.leaf.efficientFlipToPosition(posBackward);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate onDragEnd(event: HammerInput) {\n\t\tconst flipState = this.currentManualFlip;\n\t\tif (!flipState) {\n\t\t\tthis.pendingFlipDirection = FlipDirection.None;\n\t\t\tthis.pendingFlipStartingPos = 0;\n\t\t\tthis.isDragging = false;\n\t\t\tthis.clearHoverShadow();\n\t\t\treturn;\n\t\t}\n\n\t\tconst ppsX = event.velocityX * 1000; // pixels per second\n\t\tlet flipTo: FlipPosition;\n\n\t\tswitch (flipState.direction) {\n\t\t\tcase FlipDirection.Forward:\n\t\t\t\tif (\n\t\t\t\t\t(this.isLTR ? ppsX < -this.fastDeltaThreshold : ppsX > this.fastDeltaThreshold) ||\n\t\t\t\t\tflipState.leaf.flipPosition >= 0.5\n\t\t\t\t) {\n\t\t\t\t\tflipTo = 1;\n\t\t\t\t} else {\n\t\t\t\t\tflipTo = 0;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase FlipDirection.Backward:\n\t\t\t\tif (\n\t\t\t\t\t(this.isLTR ? ppsX > this.fastDeltaThreshold : ppsX < -this.fastDeltaThreshold) ||\n\t\t\t\t\tflipState.leaf.flipPosition <= 0.5\n\t\t\t\t) {\n\t\t\t\t\tflipTo = 0;\n\t\t\t\t} else {\n\t\t\t\t\tflipTo = 1;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\treturn;\n\t\t}\n\n\t\t// Mark as auto-flip and reset pending state\n\t\tflipState.isDuringAutoFlip = true;\n\t\tthis.pendingFlipDirection = FlipDirection.None;\n\t\tthis.pendingFlipStartingPos = 0;\n\t\tthis.isDragging = false;\n\n\t\t// Complete the flip asynchronously - don't block new flips!\n\t\tflipState.leaf.flipToPosition(flipTo).then(() => {\n\t\t\tthis.activeFlips.delete(flipState.leaf.index);\n\t\t});\n\t}\n\n\tprivate handleTouchStart = (e: TouchEvent) => {\n\t\tif (e.touches.length > 1) {\n\t\t\treturn;\n\t\t}\n\t\tconst touch = e.touches[0];\n\t\tthis.touchStartingPos = { x: touch.pageX, y: touch.pageY };\n\t\tthis.touchStartedInNoFlipZone = this.isInsideNoFlipZone(e.target);\n\t};\n\n\tprivate handleTouchMove = (e: TouchEvent) => {\n\t\tif (e.touches.length > 1) {\n\t\t\treturn;\n\t\t}\n\t\t// Allow horizontal scroll (e.g. carousel) when touch started inside a no-flip zone\n\t\tif (this.touchStartedInNoFlipZone) {\n\t\t\treturn;\n\t\t}\n\t\tconst touch = e.touches[0];\n\t\tconst deltaX = touch.pageX - this.touchStartingPos.x;\n\t\tconst deltaY = touch.pageY - this.touchStartingPos.y;\n\t\t// only allow vertical scrolling, as if allowing horizontal scrolling, it will interfere with the flip gesture (for touch devices)\n\t\tif (Math.abs(deltaX) > Math.abs(deltaY)) {\n\t\t\te.preventDefault();\n\t\t}\n\t};\n\n\tprivate handleMouseMove = throttle(MOUSE_MOVE_THROTTLE_MS, (event: MouseEvent) => {\n\t\tif (!this.bookElement) return;\n\t\tif (this.isDragging || this.currentManualFlip || this.activeFlips.size > 0) {\n\t\t\tthis.clearHoverShadow();\n\t\t\treturn;\n\t\t}\n\n\t\tconst rect = this.bookElement.getBoundingClientRect();\n\t\tif (rect.width <= 0) return;\n\n\t\tconst x = event.clientX - rect.left;\n\t\tconst edgeZone = rect.width * EDGE_ZONE_RATIO;\n\t\tconst isLeftEdge = x <= edgeZone;\n\t\tconst isRightEdge = x >= rect.width - edgeZone;\n\n\t\tif (!isLeftEdge && !isRightEdge) {\n\t\t\tthis.clearHoverShadow();\n\t\t\treturn;\n\t\t}\n\n\t\tconst isForward = this.isLTR ? isRightEdge : isLeftEdge;\n\t\tconst direction = isForward ? FlipDirection.Forward : FlipDirection.Backward;\n\t\tconst leaf = this.getNextAvailableLeaf(direction);\n\t\tif (!leaf) {\n\t\t\tthis.clearHoverShadow();\n\t\t\treturn;\n\t\t}\n\n\t\tconst distanceFromEdge = isForward\n\t\t\t? this.isLTR\n\t\t\t\t? rect.width - x\n\t\t\t\t: x\n\t\t\t: this.isLTR\n\t\t\t\t? x\n\t\t\t\t: rect.width - x;\n\t\tconst edgeProgress = 1 - Math.min(1, distanceFromEdge / edgeZone);\n\t\tconst hoverStrength = edgeProgress * HOVER_STRENGTH_MAX;\n\n\t\tif (this.hoveredLeaf && this.hoveredLeaf !== leaf) {\n\t\t\tthis.hoveredLeaf.setHoverShadow(0);\n\t\t}\n\t\tthis.hoveredLeaf = leaf;\n\t\tleaf.setHoverShadow(hoverStrength);\n\t});\n\n\tprivate handleMouseLeave = () => {\n\t\tthis.clearHoverShadow();\n\t};\n\n\tprivate clearHoverShadow() {\n\t\tif (this.hoveredLeaf) {\n\t\t\tthis.hoveredLeaf.setHoverShadow(0);\n\t\t\tthis.hoveredLeaf = undefined;\n\t\t}\n\t}\n\tprivate onTurned(\n\t\tcurrentVisiblePageIndices: [number] | [number, number],\n\t\tprevVisibilePageIndices?: [number] | [number, number],\n\t) {\n\t\tfor (let i = 0; i < this.pageElements.length; i++) {\n\t\t\tconst pageElement = this.pageElements[i];\n\t\t\tconst inCurrent = currentVisiblePageIndices.includes(i);\n\t\t\tconst inPrev = prevVisibilePageIndices?.includes(i);\n\t\t\tconst action = inCurrent\n\t\t\t\t? pageElement.classList.add\n\t\t\t\t: !prevVisibilePageIndices || !inPrev\n\t\t\t\t\t? () => null\n\t\t\t\t\t: pageElement.classList.remove;\n\t\t\taction.bind(pageElement.classList)(\"current-page\");\n\t\t}\n\t\t// Update leaves buffer visibility after turn completes\n\t\tthis.updateLeavesBufferVisibility();\n\t\t// Notify React/consumer so content buffer can update (e.g. mount content for new visible range)\n\t\tif (this.onPageChanged) {\n\t\t\tthis.onPageChanged(this.currentPageIndex);\n\t\t}\n\t\t// TODO expose to outside using https://github.com/open-draft/strict-event-emitter, and just be a consumer internally.\n\t\t// TODO: set prev-page / next-page classes for prev/next pages as accordingally\n\t}\n\t/**\n\t * Get the index of the current (leftmost visible) page.\n\t * Returns 0 if no pages are visible.\n\t */\n\tget currentPageIndex(): number {\n\t\treturn this.prevVisiblePageIndices?.[0] ?? 0;\n\t}\n\n\t/**\n\t * Get the total number of pages in the flipbook.\n\t */\n\tget totalPages(): number {\n\t\treturn this.pagesCount;\n\t}\n\n\t/**\n\t * Check if the book is currently showing the first page(s).\n\t */\n\tget isFirstPage(): boolean {\n\t\treturn this.currentPageIndex === 0;\n\t}\n\n\t/**\n\t * Check if the book is currently showing the last page(s).\n\t */\n\tget isLastPage(): boolean {\n\t\tconst lastPageIndex = this.pagesCount - 1;\n\t\treturn this.prevVisiblePageIndices?.includes(lastPageIndex) ?? false;\n\t}\n\n\t/**\n\t * Check if there's an active auto-flip in the given direction.\n\t */\n\tprivate hasActiveAutoFlipInDirection(direction: FlipDirection): boolean {\n\t\tfor (const state of this.activeFlips.values()) {\n\t\t\tif (state.isDuringAutoFlip && state.direction === direction) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Animate flip to the next page.\n\t * @returns Promise that resolves when the flip animation completes\n\t */\n\tasync flipNext(): Promise<void> {\n\t\t// Block if there's an active auto-flip in the opposite direction\n\t\tif (this.hasActiveAutoFlipInDirection(FlipDirection.Backward)) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst leaf = this.getNextAvailableLeaf(FlipDirection.Forward);\n\t\tif (!leaf) return;\n\n\t\tconst flipState: FlipState = {\n\t\t\tleaf,\n\t\t\tdirection: FlipDirection.Forward,\n\t\t\tstartingPos: 0,\n\t\t\tdelta: 0,\n\t\t\tisDuringAutoFlip: true,\n\t\t};\n\t\tthis.activeFlips.set(leaf.index, flipState);\n\n\t\ttry {\n\t\t\tawait leaf.flipToPosition(1);\n\t\t} finally {\n\t\t\tthis.activeFlips.delete(leaf.index);\n\t\t}\n\t}\n\n\t/**\n\t * Animate flip to the previous page.\n\t * @returns Promise that resolves when the flip animation completes\n\t */\n\tasync flipPrev(): Promise<void> {\n\t\t// Block if there's an active auto-flip in the opposite direction\n\t\tif (this.hasActiveAutoFlipInDirection(FlipDirection.Forward)) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst leaf = this.getNextAvailableLeaf(FlipDirection.Backward);\n\t\tif (!leaf) return;\n\n\t\tconst flipState: FlipState = {\n\t\t\tleaf,\n\t\t\tdirection: FlipDirection.Backward,\n\t\t\tstartingPos: 0,\n\t\t\tdelta: 0,\n\t\t\tisDuringAutoFlip: true,\n\t\t};\n\t\tthis.activeFlips.set(leaf.index, flipState);\n\n\t\ttry {\n\t\t\tawait leaf.flipToPosition(0);\n\t\t} finally {\n\t\t\tthis.activeFlips.delete(leaf.index);\n\t\t}\n\t}\n\n\t/**\n\t * Animate to a specific page index.\n\t * Flips through pages sequentially to reach the target.\n\t * @param pageIndex - The target page index (0-based)\n\t * @returns Promise that resolves when all flip animations complete\n\t */\n\tasync goToPage(pageIndex: number): Promise<void> {\n\t\tif (pageIndex < 0 || pageIndex >= this.pagesCount) {\n\t\t\tconsole.warn(\n\t\t\t\t`goToPage: Invalid page index ${pageIndex}. Must be between 0 and ${this.pagesCount - 1}.`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst targetLeafIndex = Math.floor(pageIndex / 2);\n\t\tconst currentLeafIndex = Math.floor(this.currentPageIndex / 2);\n\n\t\tif (targetLeafIndex === currentLeafIndex) {\n\t\t\treturn; // Already at the target page\n\t\t}\n\n\t\tif (targetLeafIndex > currentLeafIndex) {\n\t\t\t// Flip forward\n\t\t\tfor (let i = currentLeafIndex; i < targetLeafIndex; i++) {\n\t\t\t\tawait this.flipNext();\n\t\t\t}\n\t\t} else {\n\t\t\t// Flip backward\n\t\t\tfor (let i = currentLeafIndex; i > targetLeafIndex; i--) {\n\t\t\t\tawait this.flipPrev();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Jump to a specific page instantly without animation.\n\t * @param pageIndex - The target page index (0-based)\n\t */\n\tjumpToPage(pageIndex: number): void {\n\t\tif (pageIndex < 0 || pageIndex >= this.pagesCount) {\n\t\t\tconsole.warn(\n\t\t\t\t`jumpToPage: Invalid page index ${pageIndex}. Must be between 0 and ${this.pagesCount - 1}.`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst isLastPage = pageIndex === this.pagesCount - 1;\n\t\tconst isOddPage = pageIndex % 2 === 1;\n\t\t// If targeting the last page and it's odd (back of leaf), close the book reversed\n\t\tconst closeBookReversed = isLastPage && isOddPage;\n\n\t\t// Determine which leaf should be the last one turned.\n\t\t// A leaf's pages are visible as follows:\n\t\t// - At position 0 (not turned): its even page (front) is visible\n\t\t// - At position 1 (turned): its odd page (back) is visible\n\t\t// The visible spread after turning leaf k is [2k+1, 2k+2]:\n\t\t// back of leaf k (odd) + front of leaf k+1 (even).\n\t\t// For page 0, no leaves need to be turned (initial spread [0, 1]).\n\t\tlet lastTurnedLeafIndex: number;\n\t\tif (pageIndex <= 0) {\n\t\t\tlastTurnedLeafIndex = -1; // No leaves turned\n\t\t} else if (closeBookReversed) {\n\t\t\tlastTurnedLeafIndex = this.leaves.length - 1; // All leaves turned\n\t\t} else {\n\t\t\tlastTurnedLeafIndex = Math.floor((pageIndex - 1) / 2);\n\t\t}\n\n\t\t// Set all leaves up to lastTurnedLeafIndex as turned, all after as not turned\n\t\tfor (let i = 0; i < this.leaves.length; i++) {\n\t\t\tconst leaf = this.leaves[i];\n\t\t\tconst shouldBeTurned = i <= lastTurnedLeafIndex;\n\n\t\t\tif (shouldBeTurned && !leaf.isTurned) {\n\t\t\t\t// Turn this leaf instantly\n\t\t\t\tleaf.flipPosition = 1;\n\t\t\t\tthis.applyLeafTransform(leaf, 1);\n\t\t\t} else if (!shouldBeTurned && leaf.isTurned) {\n\t\t\t\t// Unturn this leaf instantly\n\t\t\t\tleaf.flipPosition = 0;\n\t\t\t\tthis.applyLeafTransform(leaf, 0);\n\t\t\t}\n\t\t}\n\n\t\t// Update visible page indices\n\t\tif (closeBookReversed) {\n\t\t\t// Closed reversed: only show the last page\n\t\t\tthis.prevVisiblePageIndices = [pageIndex];\n\t\t} else if (lastTurnedLeafIndex < 0) {\n\t\t\t// No leaves turned: initial spread\n\t\t\tthis.prevVisiblePageIndices = 1 < this.pagesCount ? [0, 1] : [0];\n\t\t} else {\n\t\t\tconst firstVisiblePageIndex = lastTurnedLeafIndex * 2 + 1;\n\t\t\tthis.prevVisiblePageIndices =\n\t\t\t\tfirstVisiblePageIndex + 1 < this.pagesCount\n\t\t\t\t\t? [firstVisiblePageIndex, firstVisiblePageIndex + 1]\n\t\t\t\t\t: [firstVisiblePageIndex];\n\t\t}\n\n\t\t// Update current-page classes\n\t\tfor (let i = 0; i < this.pageElements.length; i++) {\n\t\t\tconst pageElement = this.pageElements[i];\n\t\t\tif (this.prevVisiblePageIndices.includes(i)) {\n\t\t\t\tpageElement.classList.add(\"current-page\");\n\t\t\t} else {\n\t\t\t\tpageElement.classList.remove(\"current-page\");\n\t\t\t}\n\t\t}\n\n\t\t// Update leaves buffer visibility\n\t\tthis.updateLeavesBufferVisibility();\n\n\t\t// Notify callback\n\t\tif (this.onPageChanged) {\n\t\t\tthis.onPageChanged(this.currentPageIndex);\n\t\t}\n\t}\n\n\t/**\n\t * Apply transform styles to a leaf's pages for a given flip position.\n\t * Used for instant positioning (jumpToPage).\n\t */\n\tprivate applyLeafTransform(leaf: Leaf, position: number): void {\n\t\tleaf.applyTransform(position);\n\t}\n\n\t/**\n\t * Clean up event listeners and Hammer instance.\n\t * Should be called when the FlipBook is no longer needed.\n\t */\n\tdestroy() {\n\t\tif (this.hammer) {\n\t\t\tthis.hammer.destroy();\n\t\t\tthis.hammer = undefined;\n\t\t}\n\t\tif (this.bookElement) {\n\t\t\tthis.bookElement.removeEventListener(\"touchstart\", this.handleTouchStart as EventListener);\n\t\t\tthis.bookElement.removeEventListener(\"touchmove\", this.handleTouchMove as EventListener);\n\t\t\tthis.bookElement.removeEventListener(\n\t\t\t\t\"mousemove\",\n\t\t\t\tthis.handleMouseMove as unknown as EventListener,\n\t\t\t);\n\t\t\tthis.bookElement.removeEventListener(\"mouseleave\", this.handleMouseLeave as EventListener);\n\t\t}\n\t}\n}\n\nexport { FlipBook, type PageSemantics };\n"],"names":["FlipDirection","SHADOW_STRENGTH_MULTIPLIER","HIGHLIGHT_STRENGTH_MULTIPLIER","SHADOW_LIFT_PX","Leaf","index","pages","isFlipped","bookProperties","onTurned","value","progress","clamped","flipPosition","velocity","resolve","currentFlipPosition","duration","start","step","timestamp","elapsed","newPosition","throttle","position","clampedPosition","shadowFromFlip","shadowProgress","shadowStrength","highlightStrength","lift","page","isLTR","isOdd","rotateY","translateX","scaleX","origin","edge","AspectRatioImpl","width","height","aspectRatio","Size","rhsAspectRatio","rhsAspectRatioValue","DEFAULT_FAST_DELTA","EDGE_ZONE_RATIO","HOVER_STRENGTH_MAX","MOUSE_MOVE_THROTTLE_MS","FlipBook","el","secondLeafIndex","i","leaf","options","selector","debug","bookElement","pageElements","leavesCount","coverSize","leafSize","pageElement","pageIndex","leafIndex","isOddPage","isInitiallyTurned","firstVisibleLeafIndex","isCurrentPage","transform","direction","currentVisiblePageIndices","v","prevVisiblePageIndices","firstVisiblePageIndex","lastLeafFirstPage","Hammer","leftLeaf","rightLeaf","currentLeafIndex","bufferStart","bufferEnd","isWithinBuffer","debugBar","activeFlipsInfo","idx","state","event","flipState","currentPos","bookWidth","delta","posForward","posBackward","ppsX","flipTo","e","touch","deltaX","deltaY","rect","x","edgeZone","isLeftEdge","isRightEdge","isForward","distanceFromEdge","hoverStrength","prevVisibilePageIndices","inCurrent","inPrev","lastPageIndex","targetLeafIndex","isLastPage","closeBookReversed","lastTurnedLeafIndex","shouldBeTurned"],"mappings":"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"}
|