Optimize WebGL book texture reveal

This commit is contained in:
2026-06-06 16:44:15 +02:00
parent 1b8c8f8bce
commit 081cfa9902
7 changed files with 183 additions and 77 deletions
+37 -4
View File
@@ -15,6 +15,8 @@ class BookPaginationModule extends BaseModule {
this.spreads = [];
this.currentSpreadIndex = 0;
this.refreshToken = 0;
this.latestBlockId = 0;
this.latestRenderedBlockId = 0;
this.bindMethods([
'initialize',
@@ -24,6 +26,7 @@ class BookPaginationModule extends BaseModule {
'getDropCapText',
'extractDropCapText',
'extractLines',
'countLineWords',
'getLineGeometry',
'getSpread',
'getCurrentSpread',
@@ -57,12 +60,19 @@ class BookPaginationModule extends BaseModule {
);
if (!gameId || latestBlockId <= 0 || typeof this.storyHistory?.getBlocksRange !== 'function') {
this.spreads = [];
this.latestBlockId = 0;
this.latestRenderedBlockId = 0;
this.publish();
return;
}
const blocks = await this.storyHistory.getBlocksRange(gameId, 1, latestBlockId);
if (token !== this.refreshToken) return;
this.latestBlockId = latestBlockId;
this.latestRenderedBlockId = Math.max(
0,
Number(detail.latestRenderedBlockId || this.storyHistory?.latestRenderedBlockId || 0)
);
this.spreads = this.buildSpreads(blocks);
this.currentSpreadIndex = Math.max(0, Math.min(this.currentSpreadIndex, Math.max(0, this.spreads.length - 1)));
this.publish();
@@ -84,7 +94,7 @@ class BookPaginationModule extends BaseModule {
layout.lines.forEach((line, layoutLineIndex) => {
const geometry = this.getLineGeometry(cursorLine);
const lineWordCount = line.nodes.filter(node => node?.type === 'box' && node.value).length;
const lineWordCount = this.countLineWords(line);
if (!spreads[geometry.spreadIndex]) {
spreads[geometry.spreadIndex] = { index: geometry.spreadIndex, left: [], right: [] };
}
@@ -100,7 +110,8 @@ class BookPaginationModule extends BaseModule {
lineHeightPx: layout.lineHeightPx,
fontStyle: layout.fontStyle,
blockWordStart: blockWordCursor,
dropCapText: layoutLineIndex === 0 ? layout.dropCapText : ''
dropCapText: layoutLineIndex === 0 ? layout.dropCapText : '',
smallCaps: Boolean(layout.dropCap && layoutLineIndex === 0)
});
blockWordCursor += lineWordCount;
cursorLine += 1;
@@ -125,7 +136,7 @@ class BookPaginationModule extends BaseModule {
const bottomSpaceLines = role === 'chapter-heading' || role === 'section-heading' ? 1 : 0;
const lineHeightPx = Math.max(1, Number(this.metrics.typographyLineHeightPx || 1));
const fontPx = Math.max(1, Number(this.metrics.bodyFontSizePx || lineHeightPx / 1.5));
const dropCapWidth = dropCap ? lineHeightPx * 1.58 : 0;
const dropCapWidth = dropCap ? lineHeightPx * 1.72 : 0;
const indent = (isHeading || block.isFirstParagraphInChapter || block.metadata?.isFirstParagraphInChapter || block.addTopSpace)
? 0
: lineHeightPx * 1.5;
@@ -195,12 +206,32 @@ class BookPaginationModule extends BaseModule {
offset,
ratio: breaks[index].ratio || 0,
isFinal: index === breaks.length - 1,
hyphenated: Boolean(lineNodes.at(-1)?.type === 'penalty' && lineNodes.at(-1)?.penalty === 100),
align: options.align || 'justify'
});
}
return lines;
}
countLineWords(line = {}) {
const nodes = Array.isArray(line.nodes) ? line.nodes : [];
let count = 0;
let previousWasGlue = true;
nodes.forEach((node) => {
if (!node) return;
if (node.type === 'glue') {
previousWasGlue = true;
return;
}
if (node.type === 'penalty') return;
if (node.type === 'box' && node.value) {
if (previousWasGlue) count += 1;
previousWasGlue = false;
}
});
return count;
}
getLineGeometry(globalLine) {
const linesPerPage = Math.max(1, Math.floor(this.metrics.content.height / this.metrics.typographyLineHeightPx || 1));
const spreadLineCount = linesPerPage * 2;
@@ -233,7 +264,9 @@ class BookPaginationModule extends BaseModule {
detail: {
spread: this.getCurrentSpread(),
spreadIndex: this.currentSpreadIndex,
spreadCount: this.spreads.length
spreadCount: this.spreads.length,
latestBlockId: this.latestBlockId,
latestRenderedBlockId: this.latestRenderedBlockId
}
}));
}