Commit 524a1f81 authored by Jeff nan's avatar Jeff nan

feat: 更合理的逻辑

parent 67b09ef7
...@@ -68,7 +68,7 @@ ...@@ -68,7 +68,7 @@
<!-- 蓝色矩形框 --> <!-- 蓝色矩形框 -->
<div <div
v-for="(rect, index) in activeRects" v-for="(rect, index) in activeRects"
:key="'rect-' + index" :key="'rect-' + (rect._sentenceId || '') + '-' + index"
class="highlight-rect" class="highlight-rect"
:class="{ :class="{
'clickable': isPointReadMode, 'clickable': isPointReadMode,
...@@ -83,7 +83,7 @@ ...@@ -83,7 +83,7 @@
<!-- 单词高亮 --> <!-- 单词高亮 -->
<div <div
v-for="(word, index) in activeWords" v-for="(word, index) in activeWords"
:key="'word-' + index" :key="'word-' + (word._sentenceId || '') + '-' + (word._groupsIndex || 0) + '-' + (word.start || 0) + '-' + index"
class="word-highlight" class="word-highlight"
:style="getWordStyle(word)" :style="getWordStyle(word)"
></div> ></div>
......
...@@ -22,6 +22,17 @@ $(function () { ...@@ -22,6 +22,17 @@ $(function () {
}); });
function initializeApp(data) { function initializeApp(data) {
// 兜底:确保 pages 与 continuity 有效
if (!data || typeof data !== 'object') {
data = {};
}
if (!Array.isArray(data.pages)) {
data.pages = [];
}
if (!data.continuity || !Array.isArray(data.continuity) || data.continuity.length === 0) {
data.continuity = data.pages.map((_, i) => '' + i);
}
new Vue({ new Vue({
el: '#app', el: '#app',
data: { data: {
...@@ -170,7 +181,8 @@ function initializeApp(data) { ...@@ -170,7 +181,8 @@ function initializeApp(data) {
_sentence: group.sentence, _sentence: group.sentence,
_pageIndex: pageIndex, // 添加页面索引 _pageIndex: pageIndex, // 添加页面索引
_originalPageIndex: this.currentPageIndices[pageIndex], // 添加原始页面索引 _originalPageIndex: this.currentPageIndices[pageIndex], // 添加原始页面索引
_hasTimeInfo: group.start !== null && group.end !== null // 标记是否有时间信息 _hasTimeInfo: group.start !== null && group.end !== null, // 标记是否有时间信息
_sentenceId: `${this.currentPageIndices[pageIndex]}:${groupIndex}:${startTime}-${endTime}`
}; };
allRects.push(rectWithData); allRects.push(rectWithData);
...@@ -212,7 +224,8 @@ function initializeApp(data) { ...@@ -212,7 +224,8 @@ function initializeApp(data) {
_groupsIndex: wordIndex, // 添加基于当前groups的索引 _groupsIndex: wordIndex, // 添加基于当前groups的索引
_pageIndex: pageIndex, _pageIndex: pageIndex,
_originalPageIndex: this.currentPageIndices[pageIndex], _originalPageIndex: this.currentPageIndices[pageIndex],
_hasTimeInfo: word.start !== null && word.end !== null _hasTimeInfo: word.start !== null && word.end !== null,
_sentenceId: `${this.currentPageIndices[pageIndex]}:${groupIndex}:${group.start}-${group.end}`
}; };
console.log('添加单词:', { console.log('添加单词:', {
text: word.text, text: word.text,
...@@ -415,76 +428,76 @@ function initializeApp(data) { ...@@ -415,76 +428,76 @@ function initializeApp(data) {
const allRects = this.currentPageRects; const allRects = this.currentPageRects;
const allWords = this.currentPageWords; const allWords = this.currentPageWords;
// 预构建句子时间窗映射(按组和原始页索引) // 基于 page.groups 构建句子时间窗,避免因 rect 派生造成误判
const sentenceWindowByGroup = {}; const sentenceEntries = [];
allRects.forEach(rect => { this.currentPages.forEach((page, pageIndex) => {
const key = `${rect._originalPageIndex}:${rect._groupIndex}`; if (!page || !page.groups) return;
if (!sentenceWindowByGroup[key]) { const originalPageIndex = this.currentPageIndices[pageIndex];
let start = rect._start; page.groups.forEach((group, groupIndex) => {
let end = rect._end; if (!group) return;
const start = group.start;
// 若句子时间缺失,回退到所在页或当前页窗口 const end = group.end;
if (start == null || end == null) { if (start != null && end != null) {
const page = this.currentPages[rect._pageIndex]; sentenceEntries.push({
if (page) { key: `${originalPageIndex}:${groupIndex}:${start}-${end}`,
if (start == null) start = (page.start != null ? page.start : this.currentPageStartTime); start,
if (end == null) end = (page.end != null ? page.end : this.currentPageEndTime); end
} else { });
if (start == null) start = this.currentPageStartTime;
if (end == null) end = this.currentPageEndTime;
}
} }
});
});
sentenceWindowByGroup[key] = { start, end }; // 选择单一活动句子(按出现顺序第一个时间覆盖 currentTime 的句子)
let activeSentenceKey = null;
let activeSentenceStart = this.currentPageStartTime;
let activeSentenceEnd = this.currentPageEndTime;
for (let i = 0; i < sentenceEntries.length; i++) {
const entry = sentenceEntries[i];
if (currentTime >= entry.start && currentTime <= entry.end) {
activeSentenceKey = entry.key;
activeSentenceStart = entry.start;
activeSentenceEnd = entry.end;
break;
} }
}); }
// 未找到则保留现有高亮,避免闪烁
if (!activeSentenceKey) return;
// 遍历所有矩形框,检查时间(句子窗口) // 渲染活动句子的矩形(同时严格按时间窗过滤,避免同文不同时间被点亮)
allRects.forEach(rect => { allRects.forEach(rect => {
const rectStart = (rect._start != null ? rect._start : this.currentPageStartTime); const key = rect._sentenceId;
const rectEnd = (rect._end != null ? rect._end : this.currentPageEndTime); if (key !== activeSentenceKey) return;
if (currentTime >= rectStart && currentTime <= rectEnd) { const rectStart = (rect._start != null ? rect._start : activeSentenceStart);
const rectEnd = (rect._end != null ? rect._end : activeSentenceEnd);
const EPS = 0.05; // 50ms 容差
const withinActiveWindow = (rectStart >= activeSentenceStart - EPS && rectEnd <= activeSentenceEnd + EPS);
if (withinActiveWindow && currentTime >= rectStart && currentTime <= rectEnd) {
newRects.push(rect); newRects.push(rect);
} }
}); });
// 遍历所有单词,要求在“句子窗口”内;一旦点亮,保持到句子结束。 // 渲染活动句子的单词(带回补,保持到句子结束;严格按时间窗过滤)
// 健壮性:若某个短词错过点亮,但后续词已点亮,则回补此前所有词。 const groupWords = allWords.filter(w => w._sentenceId === activeSentenceKey);
const wordsByGroup = {}; if (currentTime >= activeSentenceStart && currentTime <= activeSentenceEnd) {
allWords.forEach(word => {
if (!word) return;
const key = `${word._originalPageIndex}:${word._groupIndex}`;
(wordsByGroup[key] = wordsByGroup[key] || []).push(word);
});
Object.keys(wordsByGroup).forEach(key => {
const groupWords = wordsByGroup[key];
const win = sentenceWindowByGroup[key];
const sentenceStart = win ? win.start : this.currentPageStartTime;
const sentenceEnd = win ? win.end : this.currentPageEndTime;
// 仅在句子窗口内处理该组
if (currentTime < sentenceStart || currentTime > sentenceEnd) return;
// 按 _groupsIndex 升序,确保回补逻辑按顺序进行
groupWords.sort((a, b) => (a._groupsIndex || 0) - (b._groupsIndex || 0)); groupWords.sort((a, b) => (a._groupsIndex || 0) - (b._groupsIndex || 0));
// 找到当前时间已经“开始”的最远索引(仅根据 start 判断)
let progressIndex = -1; let progressIndex = -1;
for (let i = 0; i < groupWords.length; i++) { for (let i = 0; i < groupWords.length; i++) {
const w = groupWords[i]; const w = groupWords[i];
if (w.start != null && currentTime >= w.start) { if (w.start == null) continue;
progressIndex = i; // 仅在活动句时间窗内认为该词有效,避免同文不同时间被点亮
} if (w.start < activeSentenceStart - 0.05 || w.start > activeSentenceEnd + 0.05) continue;
if (currentTime >= w.start) progressIndex = i;
} }
// 若已有任一后续词点亮,则回补至该索引之前的所有词(包含无 start 的)
if (progressIndex >= 0) { if (progressIndex >= 0) {
for (let j = 0; j <= progressIndex; j++) { for (let j = 0; j <= progressIndex; j++) {
newWords.push(groupWords[j]); const wj = groupWords[j];
if (wj.start == null) continue;
if (wj.start < activeSentenceStart - 0.05 || wj.start > activeSentenceEnd + 0.05) continue;
newWords.push(wj);
} }
} }
}); }
} }
// 只在有变化时更新,避免不必要的DOM操作 // 只在有变化时更新,避免不必要的DOM操作
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment