Browse Source

按句子高亮,日间夜间切换不影响

sequoia00 1 month ago
parent
commit
ad53690e44
1 changed files with 42 additions and 48 deletions
  1. 42 48
      static/web/viewer.html

+ 42 - 48
static/web/viewer.html

@@ -1735,6 +1735,9 @@
 
                     pdfViewer.update();
                     renderingQueue?.renderHighestPriority();
+                    if (playbackHighlightContext?.activeSentenceText || playbackHighlightContext?.activeSentenceIndex !== null) {
+                        restoreActiveSentenceHighlight();
+                    }
                 }
 
                 function getFullscreenElement() {
@@ -2307,13 +2310,43 @@
                     return {
                         textLayer,
                         textLayerIndex,
+                        fullText,
                         sentences: splitTextForHighlight(fullText),
                         searchOffset: 0,
                         lastWindowKey: '',
-                        currentRange: null
+                        currentRange: null,
+                        activeSentenceIndex: null,
+                        activeSentenceText: ''
                     };
                 }
 
+                function rebuildPlaybackHighlightContext() {
+                    const fullText = playbackHighlightContext?.fullText;
+                    if (!fullText) return false;
+                    const nextContext = buildPlaybackHighlightContext(fullText);
+                    if (!nextContext) return false;
+                    nextContext.searchOffset = playbackHighlightContext?.searchOffset || 0;
+                    nextContext.activeSentenceIndex = playbackHighlightContext?.activeSentenceIndex ?? null;
+                    nextContext.activeSentenceText = playbackHighlightContext?.activeSentenceText || '';
+                    playbackHighlightContext = nextContext;
+                    return true;
+                }
+
+                function restoreActiveSentenceHighlight(retryCount = 0) {
+                    const activeSentenceIndex = playbackHighlightContext?.activeSentenceIndex;
+                    const activeSentenceText = playbackHighlightContext?.activeSentenceText || '';
+                    if (activeSentenceIndex === null && !activeSentenceText) return;
+
+                    clearActiveTtsHighlight();
+                    if (rebuildPlaybackHighlightContext()) {
+                        highlightSentenceAtIndex(activeSentenceIndex ?? 0, activeSentenceText, true);
+                        return;
+                    }
+
+                    if (retryCount >= 12) return;
+                    setTimeout(() => restoreActiveSentenceHighlight(retryCount + 1), 120);
+                }
+
                 function updateHighlightWindow(rangeStart, rangeEnd) {
                     const ctx = playbackHighlightContext;
                     if (!ctx?.textLayerIndex) return;
@@ -2362,7 +2395,7 @@
                     }
                 }
 
-                function highlightSentenceAtIndex(sentenceIndex, sentenceText = '', audio = null) {
+                function highlightSentenceAtIndex(sentenceIndex, sentenceText = '', preserveState = false) {
                     clearActiveTtsHighlight();
                     const ctx = playbackHighlightContext;
                     if (!ctx?.textLayerIndex) return;
@@ -2379,53 +2412,14 @@
                     if (matchStart === -1) return;
 
                     const matchEnd = matchStart + targetSentence.length;
-                    ctx.searchOffset = matchEnd;
+                    if (!preserveState) {
+                        ctx.searchOffset = matchEnd;
+                    }
                     ctx.currentRange = { start: matchStart, end: matchEnd };
                     ctx.lastWindowKey = '';
-
-                    const words = targetSentence.match(/\S+/g) || [];
-                    const totalWords = words.length;
-                    const windowWordCount = Math.min(5, Math.max(1, totalWords));
-                    const wordRanges = [];
-                    let searchFrom = matchStart;
-                    for (const word of words) {
-                        const wordStart = layerText.indexOf(word, searchFrom);
-                        if (wordStart === -1) continue;
-                        const wordEnd = wordStart + word.length;
-                        wordRanges.push({ start: wordStart, end: wordEnd });
-                        searchFrom = wordEnd;
-                    }
-
-                    if (wordRanges.length === 0) {
-                        updateHighlightWindow(matchStart, Math.min(matchEnd, matchStart + 24));
-                        return;
-                    }
-
-                    const applyProgressWindow = () => {
-                        if (!audio || !Number.isFinite(audio.duration) || audio.duration <= 0) {
-                            updateHighlightWindow(wordRanges[0].start, wordRanges[Math.min(windowWordCount - 1, wordRanges.length - 1)].end);
-                            return;
-                        }
-                        const progress = Math.min(1, Math.max(0, audio.currentTime / audio.duration));
-                        const maxOffset = Math.max(0, wordRanges.length - windowWordCount);
-                        const offset = Math.min(maxOffset, Math.round(progress * maxOffset));
-                        const startWord = wordRanges[offset];
-                        const endWord = wordRanges[Math.min(wordRanges.length - 1, offset + windowWordCount - 1)];
-                        updateHighlightWindow(startWord.start, endWord.end);
-                    };
-
-                    applyProgressWindow();
-                    const runHighlightFrame = () => {
-                        if (!audio || audio.ended) {
-                            activeHighlightFrame = null;
-                            return;
-                        }
-                        if (!audio.paused) {
-                            applyProgressWindow();
-                        }
-                        activeHighlightFrame = requestAnimationFrame(runHighlightFrame);
-                    };
-                    activeHighlightFrame = requestAnimationFrame(runHighlightFrame);
+                    ctx.activeSentenceIndex = sentenceIndex;
+                    ctx.activeSentenceText = targetSentence;
+                    updateHighlightWindow(matchStart, matchEnd);
                 }
 
                 // 仅识别 PDF 页面文字层里的选择,并记录是该页第几次出现
@@ -2499,7 +2493,7 @@
 
                             try {
                                 if (signal.aborted) break;
-                                highlightSentenceAtIndex(chunk.index, chunk.text || chunk.sentence || chunk.chunk || '', audio);
+                                highlightSentenceAtIndex(chunk.index, chunk.text || chunk.sentence || chunk.chunk || '');
                                 loadingIndicator.textContent = `正在播放第 ${chunk.index + 1} 句`;
                                 loadingIndicator.style.display = 'block';
                                 await playAudio(audio);