spread-layout.js (4.0 KB)


  1 import { SPREADS } from './constants.js';
  2 import { createEmptyPlaceholder } from './html-utils.js';
  3 import { initPageFlip, animatePageFlip, setSpreadImmediate, getIsAnimating } from './page-flip-animation.js';
  4 
  5 // Track whether flip mode is enabled
  6 let flipModeEnabled = false;
  7 let currentFlipSpread = 0;
  8 
  9 // Enable/disable flip animation mode
 10 export function setFlipMode(enabled) {
 11 	flipModeEnabled = enabled;
 12 }
 13 
 14 export function isFlipModeEnabled() {
 15 	return flipModeEnabled;
 16 }
 17 
 18 // Common logic for displaying a spread (used by both editor preview and standalone viewer)
 19 export function setupSpreadLayout(container, spread, doc, useFlipAnimation = false) {
 20 	if (useFlipAnimation) {
 21 		// Initialize flip animation on first call
 22 		if (!container.querySelector('.zine-book')) {
 23 			initPageFlip(container, doc);
 24 		}
 25 		return;
 26 	}
 27 
 28 	// Original non-animated behavior
 29 	// Add empty placeholder before right page if no left page
 30 	if (!spread.left && spread.right) {
 31 		container.appendChild(createEmptyPlaceholder(doc));
 32 	}
 33 
 34 	// Move visible pages into container
 35 	const visiblePages = [spread.left, spread.right].filter(Boolean);
 36 	visiblePages.forEach(id => {
 37 		const page = doc.getElementById(id);
 38 		if (page) container.appendChild(page);
 39 	});
 40 
 41 	// Add empty placeholder after left page if no right page
 42 	if (spread.left && !spread.right) {
 43 		container.appendChild(createEmptyPlaceholder(doc));
 44 	}
 45 }
 46 
 47 // Navigate to a spread with optional flip animation
 48 export function navigateToSpread(container, spreadIndex, doc, useFlipAnimation = false, onComplete) {
 49 	if (useFlipAnimation && container.querySelector('.zine-book')) {
 50 		// Always call animatePageFlip - it now handles queueing internally
 51 		animatePageFlip(currentFlipSpread, spreadIndex, doc, onComplete);
 52 		currentFlipSpread = spreadIndex;
 53 	} else {
 54 		// Original non-animated behavior
 55 		const spread = SPREADS[spreadIndex];
 56 		container.innerHTML = '';
 57 		setupSpreadLayout(container, spread, doc, false);
 58 		if (onComplete) onComplete();
 59 	}
 60 }
 61 
 62 // Scale spread container to fit viewport
 63 export function scaleSpreadToFit(container, doc, bottomPadding = 0) {
 64 	// Find the book container if in flip mode
 65 	const bookContainer = container.querySelector('.zine-book') || container;
 66 
 67 	const vw = doc.documentElement.clientWidth;
 68 	const vh = doc.documentElement.clientHeight - bottomPadding;
 69 	const spreadWidthPx = bookContainer.offsetWidth;
 70 	const spreadHeightPx = bookContainer.offsetHeight;
 71 	if (spreadWidthPx === 0 || spreadHeightPx === 0) return;
 72 	const scaleX = (vw - 40) / spreadWidthPx;
 73 	const verticalPadding = vh <= 630 ? 40 : 130;
 74 	const scaleY = (vh - verticalPadding) / spreadHeightPx;
 75 	const scale = Math.min(scaleX, scaleY);
 76 
 77 	// Use CSS zoom for high-resolution rendering instead of transform scale
 78 	bookContainer.style.zoom = scale;
 79 
 80 	// Store the current zoom on the book container for use by updateBookPosition
 81 	bookContainer.dataset.currentZoom = scale;
 82 
 83 	// Notify parent of spread position and grid size for background grid
 84 	if (doc.defaultView?.parent && doc.defaultView.parent !== doc.defaultView) {
 85 		const rect = bookContainer.getBoundingClientRect();
 86 
 87 		// Derive grid from height (always 1 page tall = 11 cells)
 88 		// Cell aspect ratio: (2.75/7) / (4.25/11) = 30.25/29.75
 89 		const gridHeight = rect.height / 11;
 90 		const gridWidth = gridHeight * (30.25 / 29.75);
 91 
 92 		// Remove the net visual shift so grid always anchors to the un-shifted book position
 93 		// margin-left is doubled to compensate for flex re-centering, so the net visual
 94 		// shift is half the margin value; getBoundingClientRect already reflects this
 95 		const marginLeft = bookContainer.style.marginLeft || '0in';
 96 		const marginMatch = marginLeft.match(/([-.0-9]+)in/);
 97 		const netShiftPx = marginMatch ? (parseFloat(marginMatch[1]) / 2) * 96 * scale : 0;
 98 
 99 		doc.defaultView.parent.postMessage({
100 			type: 'scaleChange',
101 			gridWidth,
102 			gridHeight,
103 			spreadLeft: rect.left - netShiftPx,
104 			spreadTop: rect.top
105 		}, '*');
106 	}
107 }