Queue WebGL book reveal masks
This commit is contained in:
@@ -20,6 +20,8 @@ class PlaybackCoordinatorModule extends BaseModule {
|
||||
'play',
|
||||
'calculateWordTimings',
|
||||
'animateWords',
|
||||
'isWebGLPlaybackMode',
|
||||
'scheduleWebGLReveal',
|
||||
'waitForAudioStart',
|
||||
'completeSentenceVisual',
|
||||
'accelerateActiveWordAnimations',
|
||||
@@ -213,7 +215,7 @@ class PlaybackCoordinatorModule extends BaseModule {
|
||||
* @returns {Promise<void>} - Resolves when animation completes
|
||||
*/
|
||||
async animateWords(sentence) {
|
||||
if (!sentence.element || !sentence.animation || !sentence.animation.wordTimings) {
|
||||
if (!sentence.animation || !sentence.animation.wordTimings) {
|
||||
console.error('PlaybackCoordinator: Missing animation data');
|
||||
return Promise.resolve();
|
||||
}
|
||||
@@ -224,6 +226,15 @@ class PlaybackCoordinatorModule extends BaseModule {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (this.isWebGLPlaybackMode()) {
|
||||
return this.scheduleWebGLReveal(sentence, animQueue);
|
||||
}
|
||||
|
||||
if (!sentence.element) {
|
||||
console.error('PlaybackCoordinator: Missing DOM element for 2D animation');
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const wordElements = sentence.element.querySelectorAll('.word');
|
||||
let wordTimings = sentence.animation.wordTimings;
|
||||
let cueTimings = sentence.animation.cueTimings || [];
|
||||
@@ -302,6 +313,57 @@ class PlaybackCoordinatorModule extends BaseModule {
|
||||
});
|
||||
}
|
||||
|
||||
isWebGLPlaybackMode() {
|
||||
return document.body?.dataset?.webglUiMode === '3d'
|
||||
|| document.body?.classList?.contains('webgl-mode');
|
||||
}
|
||||
|
||||
scheduleWebGLReveal(sentence, animQueue) {
|
||||
let wordTimings = Array.isArray(sentence.animation?.wordTimings)
|
||||
? sentence.animation.wordTimings
|
||||
: [];
|
||||
let cueTimings = Array.isArray(sentence.animation?.cueTimings)
|
||||
? sentence.animation.cueTimings
|
||||
: [];
|
||||
if (wordTimings.length === 0) {
|
||||
const words = String(sentence.text || '').match(/\S+/g) || [];
|
||||
const calculated = this.calculateWordTimings(words, sentence.tts?.duration || sentence.animation?.totalDuration || 0);
|
||||
wordTimings = calculated.wordTimings;
|
||||
cueTimings = [];
|
||||
}
|
||||
|
||||
document.dispatchEvent(new CustomEvent('book-texture:reveal-block', {
|
||||
detail: {
|
||||
id: sentence.id,
|
||||
blockId: sentence.blockId ?? sentence.metadata?.blockId ?? null,
|
||||
wordTimings,
|
||||
cueTimings,
|
||||
totalDuration: sentence.animation?.totalDuration || 0
|
||||
}
|
||||
}));
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const totalDuration = wordTimings.length > 0
|
||||
? Math.max(...wordTimings.map(timing => Number(timing.delay || 0) + Number(timing.duration || 0)))
|
||||
: 0;
|
||||
|
||||
cueTimings.forEach(cue => {
|
||||
animQueue.schedule(() => {
|
||||
document.dispatchEvent(new CustomEvent('story:media-cue', {
|
||||
detail: {
|
||||
sentenceId: sentence.id,
|
||||
...cue
|
||||
}
|
||||
}));
|
||||
}, cue.delay || 0);
|
||||
});
|
||||
|
||||
animQueue.schedule(() => {
|
||||
resolve();
|
||||
}, totalDuration + 100);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate word-level timing to match total TTS duration
|
||||
* This is a utility method that can be called by SentenceQueue during preparation
|
||||
|
||||
Reference in New Issue
Block a user