Stabilize WebGL title and timeline texture flow
This commit is contained in:
@@ -7,10 +7,11 @@ import { BaseModule } from './base-module.js';
|
||||
class BookTextureRendererModule extends BaseModule {
|
||||
constructor() {
|
||||
super('book-texture-renderer', 'Book Texture Renderer');
|
||||
this.dependencies = ['book-page-format', 'book-pagination', 'localization', 'webgl-page-cache'];
|
||||
this.dependencies = ['book-page-format', 'book-pagination', 'localization', 'game-config', 'webgl-page-cache'];
|
||||
this.pageFormat = null;
|
||||
this.pagination = null;
|
||||
this.localization = null;
|
||||
this.gameConfig = null;
|
||||
this.pageCache = null;
|
||||
this.metrics = null;
|
||||
this.canvases = {
|
||||
@@ -103,6 +104,7 @@ class BookTextureRendererModule extends BaseModule {
|
||||
this.pageFormat = this.getModule('book-page-format');
|
||||
this.pagination = this.getModule('book-pagination');
|
||||
this.localization = this.getModule('localization');
|
||||
this.gameConfig = this.getModule('game-config');
|
||||
this.pageCache = this.getModule('webgl-page-cache');
|
||||
window.BookTextureRendererDebug = {
|
||||
pipelineTimings: this.pipelineTimings
|
||||
@@ -119,6 +121,7 @@ class BookTextureRendererModule extends BaseModule {
|
||||
const latestRenderedBlockId = Math.max(0, Number(event.detail?.latestRenderedBlockId || 0));
|
||||
const visibility = event.detail?.visibility || 'current';
|
||||
this.currentSpread = spread || { left: [], right: [] };
|
||||
const timelineOwnsPlayback = window.BookPlaybackTimeline?.ownsPageFlipCommit === true;
|
||||
if (document.documentElement.dataset.webglPageFlipActive === 'true' && this.activeAnimations.size === 0) {
|
||||
this.markPipelineTiming('spreadUpdate:skip-during-flip', {
|
||||
spreadIndex,
|
||||
@@ -129,8 +132,19 @@ class BookTextureRendererModule extends BaseModule {
|
||||
if (latestBlockId && Number(latestBlockId) > latestRenderedBlockId) {
|
||||
this.markPendingReveal(latestBlockId);
|
||||
const id = String(latestBlockId);
|
||||
if (timelineOwnsPlayback && visibility !== 'future-ready') {
|
||||
this.markPipelineTiming('spreadUpdate:skip-timeline-owned-reveal', {
|
||||
spreadIndex,
|
||||
latestBlockId: id,
|
||||
visibility
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (visibility === 'future-ready' && !this.activeAnimations.has(id)) {
|
||||
this.drawSpread(this.stripUnrenderedLines(this.currentSpread, latestRenderedBlockId), ['left', 'right']);
|
||||
this.drawSpread(this.stripUnrenderedLines(this.currentSpread, latestRenderedBlockId), ['left', 'right'], {
|
||||
phase: 'prepare',
|
||||
publishEvent: !timelineOwnsPlayback
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (this.activeAnimations.has(id)) {
|
||||
@@ -145,6 +159,15 @@ class BookTextureRendererModule extends BaseModule {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (timelineOwnsPlayback && visibility !== 'future-ready' && latestBlockId) {
|
||||
this.markPipelineTiming('spreadUpdate:skip-timeline-owned-commit', {
|
||||
spreadIndex,
|
||||
latestBlockId,
|
||||
latestRenderedBlockId,
|
||||
visibility
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.drawSpread(this.currentSpread);
|
||||
});
|
||||
this.addEventListener(document, 'book-texture:fast-forward', this.fastForwardAnimations);
|
||||
@@ -230,7 +253,7 @@ class BookTextureRendererModule extends BaseModule {
|
||||
const hasReveal = this.revealPublishBlockIds && this.revealPublishBlockIds.size > 0;
|
||||
const phase = this.getDrawPhase(options);
|
||||
const drawSignature = this.getDrawSignature(this.currentSpread, sidesToDraw);
|
||||
if (phase !== 'prepare' && !hasReveal && drawSignature === this.lastDrawSignature) {
|
||||
if (options.force !== true && phase !== 'prepare' && !hasReveal && drawSignature === this.lastDrawSignature) {
|
||||
const now = performance.now();
|
||||
if (now - this.lastDrawSkipLoggedAt > 1000) {
|
||||
this.lastDrawSkipLoggedAt = now;
|
||||
@@ -326,11 +349,17 @@ class BookTextureRendererModule extends BaseModule {
|
||||
const ctx = this.contexts[side];
|
||||
if (!ctx || !this.metrics) return;
|
||||
const content = this.getPageContent(side);
|
||||
const titleText = document.getElementById('game_title')?.textContent?.trim() || '';
|
||||
const authorText = document.getElementById('game_author')?.textContent?.trim() || '';
|
||||
const subtitleText = document.getElementById('game_subtitle')?.textContent?.trim() || '';
|
||||
const metadata = this.gameConfig?.getMetadata?.() || {};
|
||||
const titleText = document.getElementById('game_title')?.textContent?.trim() || metadata.title || '';
|
||||
const authorText = document.getElementById('game_author')?.textContent?.trim()
|
||||
|| (metadata.author ? this.localization?.t?.('title.byAuthor', { author: metadata.author }) : '')
|
||||
|| '';
|
||||
const subtitleText = document.getElementById('game_subtitle')?.textContent?.trim() || metadata.subtitle || '';
|
||||
const ornamentText = document.querySelector('#start_prompt .separator, #start_prompt .ornament, #start_prompt [class*="separator"]')?.textContent?.trim() || '';
|
||||
const legalText = document.getElementById('game_legal_text')?.textContent?.trim() || '';
|
||||
const legalText = document.getElementById('game_legal_text')?.textContent?.trim() || [
|
||||
metadata.version ? this.localization?.t?.('title.version', { version: metadata.version }) : '',
|
||||
metadata.copyright || ''
|
||||
].filter(Boolean).join(' | ');
|
||||
const centerX = content.x + content.width * 0.5;
|
||||
const font = this.metrics.typography.fontFamily;
|
||||
|
||||
@@ -658,7 +687,8 @@ class BookTextureRendererModule extends BaseModule {
|
||||
blockId: region.blockId,
|
||||
lineIndex: region.lineIndex,
|
||||
rect: region.rect,
|
||||
timing: region.timing
|
||||
timing: region.timing,
|
||||
timingArea: region.timingArea || region.area || 0
|
||||
})),
|
||||
bounds: {
|
||||
x: bounds.x / this.metrics.width,
|
||||
@@ -707,7 +737,7 @@ class BookTextureRendererModule extends BaseModule {
|
||||
}
|
||||
|
||||
assignRevealTiming(blockRegions = [], animation = {}) {
|
||||
const totalDuration = Math.max(
|
||||
const requestedTotalDuration = Math.max(
|
||||
Number(animation.totalDuration || 0),
|
||||
...((Array.isArray(animation.wordTimings) ? animation.wordTimings : []).map(timing => Number(timing.delay || 0) + Number(timing.duration || 0)))
|
||||
);
|
||||
@@ -723,32 +753,22 @@ class BookTextureRendererModule extends BaseModule {
|
||||
const textRegions = sortedRegions.filter(region => !(region.fixedDurationMs > 0));
|
||||
const fixedRegions = sortedRegions.filter(region => region.fixedDurationMs > 0);
|
||||
let fallbackDelay = 0;
|
||||
const totalArea = textRegions.reduce((sum, region) => sum + Math.max(1, region.area), 0);
|
||||
const wordTimings = Array.isArray(animation.wordTimings) ? animation.wordTimings : [];
|
||||
const canUseLineWordSpans = wordTimings.length > 0
|
||||
&& textRegions.every(region => Number.isFinite(Number(region.blockWordStart)) && Number(region.blockWordCount) > 0);
|
||||
|
||||
if (canUseLineWordSpans) {
|
||||
textRegions.forEach((region) => {
|
||||
const timing = this.getLineTimingFromWords(region, wordTimings);
|
||||
timedRegions.push({
|
||||
...region,
|
||||
timing
|
||||
});
|
||||
fallbackDelay = Math.max(fallbackDelay, timing.delay + timing.duration);
|
||||
const totalArea = textRegions.reduce((sum, region) => sum + Math.max(1, region.timingArea || region.area), 0);
|
||||
const lineHeight = Math.max(1, Number(this.metrics?.typographyLineHeightPx || 1));
|
||||
const estimatedTextWidth = totalArea / lineHeight;
|
||||
const totalDuration = requestedTotalDuration > 1
|
||||
? requestedTotalDuration
|
||||
: Math.max(800, estimatedTextWidth * 16);
|
||||
textRegions.forEach((region) => {
|
||||
const duration = totalArea > 0
|
||||
? Math.max(1, totalDuration * (Math.max(1, region.timingArea || region.area) / totalArea))
|
||||
: Math.max(1, totalDuration / Math.max(1, textRegions.length));
|
||||
timedRegions.push({
|
||||
...region,
|
||||
timing: { delay: fallbackDelay, duration }
|
||||
});
|
||||
} else {
|
||||
textRegions.forEach((region) => {
|
||||
const duration = totalArea > 0
|
||||
? Math.max(1, totalDuration * (Math.max(1, region.area) / totalArea))
|
||||
: Math.max(1, totalDuration / Math.max(1, textRegions.length));
|
||||
timedRegions.push({
|
||||
...region,
|
||||
timing: { delay: fallbackDelay, duration }
|
||||
});
|
||||
fallbackDelay += duration;
|
||||
});
|
||||
}
|
||||
fallbackDelay += duration;
|
||||
});
|
||||
|
||||
fixedRegions.forEach((region) => {
|
||||
timedRegions.push({
|
||||
@@ -813,6 +833,8 @@ class BookTextureRendererModule extends BaseModule {
|
||||
const bottom = Math.min(this.metrics.height, y + height + padding);
|
||||
const rectWidth = Math.max(1, right - left);
|
||||
const rectHeight = Math.max(1, bottom - top);
|
||||
const timingWidth = Math.max(1, Number(lineRecord.timingWidthPx || width || rectWidth));
|
||||
const timingHeight = Math.max(1, Number(lineRecord.timingHeightPx || height || rectHeight));
|
||||
return {
|
||||
side,
|
||||
spreadIndex: Math.max(0, Number((spreadIndex ?? Math.floor(Number(lineRecord.pageIndex || 0) / 2)) || 0)),
|
||||
@@ -822,6 +844,7 @@ class BookTextureRendererModule extends BaseModule {
|
||||
blockWordCount: Number(lineRecord.lineWordCount ?? 0),
|
||||
fixedDurationMs,
|
||||
area: rectWidth * rectHeight,
|
||||
timingArea: timingWidth * timingHeight,
|
||||
pixelRect: { x: left, y: top, right, bottom },
|
||||
rect: {
|
||||
x: left / this.metrics.width,
|
||||
@@ -975,7 +998,7 @@ class BookTextureRendererModule extends BaseModule {
|
||||
phase,
|
||||
publishEvent: options.publishEvent !== false
|
||||
});
|
||||
if (phase !== 'prepare') this.preloadAdditionalRevealSpreads(id, spread);
|
||||
this.preloadAdditionalRevealSpreads(id, spread);
|
||||
if (phase === 'prepare' && published) {
|
||||
this.pageCache?.rememberPreparedRevealPlan?.(id, {
|
||||
...published,
|
||||
|
||||
Reference in New Issue
Block a user