commit 9e3323f1342ea8c2fc5e2116d4a4b949a40ed270
parent e85bdcd0cc37a32978a1a5da2ee0721b9af3e846
Author: Hunter
Date: Mon, 2 Feb 2026 18:40:22 -0500
add page flip buffering
Diffstat:
3 files changed, 56 insertions(+), 9 deletions(-)
diff --git a/resources/page-flip-animation.js b/resources/page-flip-animation.js
@@ -1,6 +1,7 @@
// Page flip animation state
let isAnimating = false;
let currentSpreadIndex = 0;
+let pendingFlipQueue = [];
// Get animation state
export function getIsAnimating() {
@@ -94,10 +95,20 @@ function updateLeafStates(spreadIndex, doc) {
}
-// Animate page flip
-export function animatePageFlip(fromSpread, toSpread, container, doc, onComplete) {
- if (isAnimating) return;
+// Process next queued flip if any
+function processNextFlip() {
+ if (pendingFlipQueue.length === 0) {
+ isAnimating = false;
+ return;
+ }
+
+ const nextFlip = pendingFlipQueue.shift();
+ // Use current spread index as fromSpread since we're now at a different position
+ executePageFlip(currentSpreadIndex, nextFlip.toSpread, nextFlip.container, nextFlip.doc, nextFlip.onComplete);
+}
+// Execute a single page flip (internal function)
+function executePageFlip(fromSpread, toSpread, container, doc, onComplete) {
isAnimating = true;
currentSpreadIndex = toSpread;
@@ -120,16 +131,25 @@ export function animatePageFlip(fromSpread, toSpread, container, doc, onComplete
});
// Clean up after animation
+ let cleanupCalled = false;
const cleanup = () => {
+ if (cleanupCalled) return;
+ cleanupCalled = true;
+
leafToFlip.dataset.state = 'open';
leafToFlip.style.transition = '';
// Set z-index for open state (lower than closed leaves)
leafToFlip.style.zIndex = String(fromSpread + 1);
- isAnimating = false;
if (onComplete) onComplete();
leafToFlip.removeEventListener('transitionend', cleanup);
+ clearTimeout(timeoutId);
+ // Process next flip in queue
+ processNextFlip();
};
leafToFlip.addEventListener('transitionend', cleanup);
+
+ // Fallback timeout in case transitionend doesn't fire
+ const timeoutId = setTimeout(cleanup, 800);
}
} else {
// Flipping backward (left to right)
@@ -147,20 +167,46 @@ export function animatePageFlip(fromSpread, toSpread, container, doc, onComplete
});
// Clean up after animation
+ let cleanupCalled = false;
const cleanup = () => {
+ if (cleanupCalled) return;
+ cleanupCalled = true;
+
leafToFlip.dataset.state = 'closed';
leafToFlip.style.transition = '';
// Set z-index for closed state (higher than open leaves)
leafToFlip.style.zIndex = String(20 - toSpread);
- isAnimating = false;
if (onComplete) onComplete();
leafToFlip.removeEventListener('transitionend', cleanup);
+ clearTimeout(timeoutId);
+ // Process next flip in queue
+ processNextFlip();
};
leafToFlip.addEventListener('transitionend', cleanup);
+
+ // Fallback timeout in case transitionend doesn't fire
+ const timeoutId = setTimeout(cleanup, 800);
}
}
}
+// Animate page flip with input buffering
+export function animatePageFlip(fromSpread, toSpread, container, doc, onComplete) {
+ // If already animating, queue this flip request
+ if (isAnimating) {
+ // Check if there's already a queued flip to the same target - if so, skip this one
+ // This prevents the queue from growing unnecessarily when users spam the same direction
+ const lastQueued = pendingFlipQueue[pendingFlipQueue.length - 1];
+ if (!lastQueued || lastQueued.toSpread !== toSpread) {
+ pendingFlipQueue.push({ fromSpread, toSpread, container, doc, onComplete });
+ }
+ return;
+ }
+
+ // Start the flip immediately
+ executePageFlip(fromSpread, toSpread, container, doc, onComplete);
+}
+
// Set current spread without animation (for initial load)
export function setSpreadImmediate(spreadIndex, doc) {
currentSpreadIndex = spreadIndex;
diff --git a/resources/spread-layout.js b/resources/spread-layout.js
@@ -47,10 +47,9 @@ export function setupSpreadLayout(container, spread, doc, useFlipAnimation = fal
// Navigate to a spread with optional flip animation
export function navigateToSpread(container, spreadIndex, doc, useFlipAnimation = false, onComplete) {
if (useFlipAnimation && container.querySelector('.zine-book')) {
- if (!getIsAnimating()) {
- animatePageFlip(currentFlipSpread, spreadIndex, container, doc, onComplete);
- currentFlipSpread = spreadIndex;
- }
+ // Always call animatePageFlip - it now handles queueing internally
+ animatePageFlip(currentFlipSpread, spreadIndex, container, doc, onComplete);
+ currentFlipSpread = spreadIndex;
} else {
// Original non-animated behavior
const spread = SPREADS[spreadIndex];
diff --git a/resources/zine-styles.js b/resources/zine-styles.js
@@ -61,6 +61,7 @@ export const ZINE_FLIP_CSS = `
transform-origin: left center;
transform-style: preserve-3d;
transition: none;
+ will-change: transform;
}
/* When flipping, use high z-index so it's on top during animation */
@@ -76,6 +77,7 @@ export const ZINE_FLIP_CSS = `
height: 100%;
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
+ transform: translateZ(0);
}
.zine-leaf-front {