Enforce explicit WebGL book playback timeline

This commit is contained in:
2026-06-10 09:35:00 +02:00
parent 5a84923884
commit ce8147b5b1
7 changed files with 199 additions and 185 deletions
+48 -38
View File
@@ -79,7 +79,6 @@ class BookTextureRendererModule extends BaseModule {
'prepareRevealBlock',
'preloadAdditionalRevealSpreads',
'spreadContainsBlock',
'hasPreparedRevealBlock',
'createAnimationState',
'getDrawPhase',
'publishPreparedReveal',
@@ -141,12 +140,6 @@ class BookTextureRendererModule extends BaseModule {
}
this.drawSpread(this.currentSpread);
});
this.addEventListener(document, 'book-texture:reveal-block', (event) => {
this.startRevealAnimation(event.detail || {});
});
this.addEventListener(document, 'book-texture:prepare-reveal-block', (event) => {
this.prepareRevealBlock(event.detail || {});
});
this.addEventListener(document, 'book-texture:fast-forward', this.fastForwardAnimations);
this.addEventListener(document, 'ui:command', (event) => {
if (event.detail?.type === 'continue') this.fastForwardAnimations();
@@ -910,13 +903,17 @@ class BookTextureRendererModule extends BaseModule {
const cached = this.pageCache.takePreparedRevealPlan(id);
this.activeAnimations.set(id, this.createAnimationState(blockId, wordTimings, detail));
this.pendingRevealBlockIds.delete(id);
this.publishPreparedReveal(cached);
this.publishPreparedReveal(cached, options);
this.markPipelineTiming('prepareRevealBlock:end', {
blockId: id,
wordTimingCount: wordTimings.length,
reusedPreparedCanvas: true
});
return;
return {
...cached,
phase: 'activate',
preparedFromCache: true
};
}
this.activeAnimations.set(id, this.createAnimationState(blockId, wordTimings, detail));
@@ -924,7 +921,10 @@ class BookTextureRendererModule extends BaseModule {
this.revealPublishBlockIds = new Set([id]);
const spread = detail.spread || this.currentSpread || this.pagination?.getCurrentSpread?.();
const sides = ['left', 'right'];
const published = this.drawSpread(spread, sides, { phase });
const published = this.drawSpread(spread, sides, {
phase,
publishEvent: options.publishEvent !== false
});
if (phase !== 'prepare') this.preloadAdditionalRevealSpreads(id, spread);
if (phase === 'prepare' && published) {
this.pageCache?.rememberPreparedRevealPlan?.(id, {
@@ -939,6 +939,12 @@ class BookTextureRendererModule extends BaseModule {
wordTimingCount: wordTimings.length,
phase
});
return published ? {
...published,
blockId,
wordTimings,
totalDuration: detail.totalDuration || 0
} : null;
}
preloadAdditionalRevealSpreads(blockId, primarySpread = null) {
@@ -960,32 +966,29 @@ class BookTextureRendererModule extends BaseModule {
});
}
hasPreparedRevealBlock(blockId) {
const id = String(blockId ?? '');
return Boolean(id && this.pageCache?.hasPreparedRevealPlan?.(id));
}
publishPreparedReveal(prepared) {
if (!prepared) return;
publishPreparedReveal(prepared, options = {}) {
if (!prepared) return null;
this.markPipelineTiming('publishPreparedReveal', {
blockId: prepared.blockId,
sides: prepared.sides || [],
hasReveal: Boolean(prepared.reveal && Object.keys(prepared.reveal).length)
});
document.dispatchEvent(new CustomEvent('webgl-book:page-texture-records', {
detail: {
metrics: prepared.metrics,
hitMaps: prepared.hitMaps || this.hitMaps,
records: prepared.records || this.buildPageTextureRecords(prepared.sides || ['left', 'right'], prepared),
reveal: prepared.reveal || {},
pageMeta: prepared.pageMeta || {},
phase: 'activate',
preparedFromCache: true
}
}));
const detail = {
metrics: prepared.metrics,
hitMaps: prepared.hitMaps || this.hitMaps,
records: prepared.records || this.buildPageTextureRecords(prepared.sides || ['left', 'right'], prepared),
reveal: prepared.reveal || {},
pageMeta: prepared.pageMeta || {},
phase: 'activate',
preparedFromCache: true
};
if (options.publishEvent !== false) {
document.dispatchEvent(new CustomEvent('webgl-book:page-texture-records', { detail }));
}
return detail;
}
startPreparedRevealAnimation(blockId) {
startPreparedRevealAnimation(blockId, options = {}) {
const id = String(blockId ?? '');
const animation = this.activeAnimations.get(id);
if (!animation) return false;
@@ -996,13 +999,18 @@ class BookTextureRendererModule extends BaseModule {
animation.startedAt = performance.now();
animation.prepared = false;
animation.completed = false;
document.dispatchEvent(new CustomEvent('webgl-book:page-reveal-start', {
detail: {
blockId: animation.blockId
}
}));
if (options.publishEvent !== false) {
document.dispatchEvent(new CustomEvent('webgl-book:page-reveal-start', {
detail: {
blockId: animation.blockId
}
}));
}
this.requestAnimationFrame();
return true;
return {
blockId: animation.blockId,
wordTimingCount: Array.isArray(animation.wordTimings) ? animation.wordTimings.length : 0
};
}
fastForwardAnimations() {
@@ -1153,9 +1161,11 @@ class BookTextureRendererModule extends BaseModule {
regionCounts,
phase
});
document.dispatchEvent(new CustomEvent('webgl-book:page-texture-records', {
detail
}));
if (options.publishEvent !== false) {
document.dispatchEvent(new CustomEvent('webgl-book:page-texture-records', {
detail
}));
}
return detail;
}