commit f2f3e4dd26daabc3eb5db165f9e63832d4dffb63
parent 98ec6608fb25192a41230207b485b4ea17ee5744
Author: Hunter
Date: Wed, 4 Feb 2026 16:47:58 -0500
fix judder without blur
Diffstat:
3 files changed, 37 insertions(+), 69 deletions(-)
diff --git a/resources/page-flip-animation.js b/resources/page-flip-animation.js
@@ -70,58 +70,41 @@ export function initPageFlip(container, doc) {
updateBookPosition(0, false);
}
-// Update the book container horizontal position based on spread
-// Spreads 0 and 4 (single pages) should be centered, others at normal position
-function updateBookPosition(spreadIndex, animated = true) {
- if (!bookContainer) {
- console.error('updateBookPosition: bookContainer is null!');
- return;
- }
-
+// Calculate the shift amount for a given spread
+function getShiftAmountForSpread(spreadIndex) {
// Spread 0 (front cover): single right page, shift left to center
// Spread 4 (back cover): single left page, shift right to center
- // Half page width = 1.375in
- let shiftAmount = '0in';
+ // margin-left is doubled to compensate for flex re-centering, so the net visual
+ // shift is half the margin value (net visual shift = half a page width = 1.375in)
if (spreadIndex === 0) {
- shiftAmount = '-1.375in';
+ return '-2.75in';
} else if (spreadIndex === 4) {
- shiftAmount = '1.375in';
+ return '2.75in';
}
+ return '0in';
+}
- const currentTransform = bookContainer.style.transform;
- const currentTransition = bookContainer.style.transition;
- console.log(`updateBookPosition(spread=${spreadIndex}, animated=${animated})`);
- console.log(` Current transform: "${currentTransform}"`);
- console.log(` Current transition: "${currentTransition}"`);
- console.log(` Target: translateX(${shiftAmount})`);
-
- if (animated) {
- // Clear any existing transition first to ensure clean state
- bookContainer.style.transition = '';
- // Force a reflow
- bookContainer.offsetHeight;
+// Update the book container horizontal position based on spread
+// Spreads 0 and 4 (single pages) should be centered, others at normal position
+// Uses margin-left instead of translateX to avoid conflicts with preserve-3d and zoom
+function updateBookPosition(spreadIndex, animated = true) {
+ if (!bookContainer) return;
- // Now set the transition for this animation
- bookContainer.style.transition = 'transform 0.6s cubic-bezier(0.645, 0.045, 0.355, 1.000)';
- console.log(` Set transition, will animate in rAF`);
+ const shiftAmount = getShiftAmountForSpread(spreadIndex);
- // Use requestAnimationFrame to set transform (same as leaf flip animation)
- // This ensures both animations start in the same frame
- requestAnimationFrame(() => {
- console.log(` rAF: Setting transform to translateX(${shiftAmount})`);
- bookContainer.style.transform = `translateX(${shiftAmount})`;
- });
+ if (animated) {
+ bookContainer.style.transition = 'margin-left 0.6s cubic-bezier(0.645, 0.045, 0.355, 1.000)';
+ bookContainer.style.marginLeft = shiftAmount;
// Remove transition after animation completes
setTimeout(() => {
bookContainer.style.transition = '';
- }, 600);
+ }, 650);
} else {
bookContainer.style.transition = 'none';
- // For non-animated, use explicit translateX or none
- const newTransform = shiftAmount !== '0in' ? `translateX(${shiftAmount})` : 'translateX(0in)';
- console.log(` Non-animated: Setting transform to ${newTransform}`);
- bookContainer.style.transform = newTransform;
+ bookContainer.style.marginLeft = shiftAmount;
+ bookContainer.offsetHeight;
+ bookContainer.style.transition = '';
}
}
diff --git a/resources/spread-layout.js b/resources/spread-layout.js
@@ -79,13 +79,6 @@ export function scaleSpreadToFit(container, doc, bottomPadding = 0) {
// Store the current zoom on the book container for use by updateBookPosition
bookContainer.dataset.currentZoom = scale;
- // Preserve any existing translateX from book positioning
- const currentTransform = bookContainer.style.transform || '';
- const translateMatch = currentTransform.match(/translateX\([^)]+\)/);
- const translateX = translateMatch ? translateMatch[0] : '';
-
- bookContainer.style.transform = translateX || '';
-
// Notify parent of spread position and grid size for background grid
if (doc.defaultView?.parent && doc.defaultView.parent !== doc.defaultView) {
const rect = bookContainer.getBoundingClientRect();
@@ -95,16 +88,18 @@ export function scaleSpreadToFit(container, doc, bottomPadding = 0) {
const gridHeight = rect.height / 11;
const gridWidth = gridHeight * (30.25 / 29.75);
- // Remove translateX shift so grid always anchors to the un-shifted book position
- // With CSS zoom, translateX values are in the zoomed coordinate space
- const translateInchMatch = translateX.match(/translateX\(([^)]+)in\)/);
- const translatePx = translateInchMatch ? parseFloat(translateInchMatch[1]) * 96 * scale : 0;
+ // Remove the net visual shift so grid always anchors to the un-shifted book position
+ // margin-left is doubled to compensate for flex re-centering, so the net visual
+ // shift is half the margin value; getBoundingClientRect already reflects this
+ const marginLeft = bookContainer.style.marginLeft || '0in';
+ const marginMatch = marginLeft.match(/([-.0-9]+)in/);
+ const netShiftPx = marginMatch ? (parseFloat(marginMatch[1]) / 2) * 96 * scale : 0;
doc.defaultView.parent.postMessage({
type: 'scaleChange',
gridWidth,
gridHeight,
- spreadLeft: rect.left - translatePx,
+ spreadLeft: rect.left - netShiftPx,
spreadTop: rect.top
}, '*');
}
diff --git a/resources/viewer-generator.js b/resources/viewer-generator.js
@@ -114,37 +114,27 @@ export function generateViewerCode() {
// Spread 0 (front cover): single right page, shift left to center
// Spread 4 (back cover): single left page, shift right to center
- // Half page width = 1.375in
+ // Doubled to compensate for flex re-centering (net visual shift = half)
let shiftAmount = '0in';
if (spreadIndex === 0) {
- shiftAmount = '-1.375in';
+ shiftAmount = '-2.75in';
} else if (spreadIndex === 4) {
- shiftAmount = '1.375in';
+ shiftAmount = '2.75in';
}
if (animated) {
- // Clear any existing transition first to ensure clean state
- bookContainer.style.transition = '';
- // Force a reflow
- bookContainer.offsetHeight;
-
- // Now set the transition for this animation
- bookContainer.style.transition = 'transform 0.6s cubic-bezier(0.645, 0.045, 0.355, 1.000)';
-
- // Use requestAnimationFrame to set transform (same as leaf flip animation)
- // This ensures both animations start in the same frame
- requestAnimationFrame(function() {
- bookContainer.style.transform = 'translateX(' + shiftAmount + ')';
- });
+ bookContainer.style.transition = 'margin-left 0.6s cubic-bezier(0.645, 0.045, 0.355, 1.000)';
+ bookContainer.style.marginLeft = shiftAmount;
// Remove transition after animation completes
setTimeout(function() {
bookContainer.style.transition = '';
- }, 600);
+ }, 650);
} else {
bookContainer.style.transition = 'none';
- // For non-animated, use explicit translateX
- bookContainer.style.transform = shiftAmount !== '0in' ? 'translateX(' + shiftAmount + ')' : 'translateX(0in)';
+ bookContainer.style.marginLeft = shiftAmount;
+ bookContainer.offsetHeight;
+ bookContainer.style.transition = '';
}
}