Commit c0996581 authored by 15011343103's avatar 15011343103

feat: 字幕逐字高亮

parent 243286c8
......@@ -193,6 +193,26 @@ body::-webkit-scrollbar, html::-webkit-scrollbar {
line-height: 1.5;
}
.subtitle-text span {
display: inline-block;
margin: 0 8px;
transition: all 0.2s ease;
}
.subtitle-text .highlighted-word {
color: #ffeb3b;
text-shadow: 0 0 10px rgba(255, 235, 59, 0.8);
transform: scale(1.05);
font-weight: 900;
animation: wordPulse 0.5s ease-in-out;
}
@keyframes wordPulse {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1.05); }
}
.controls {
display: flex;
justify-content: center;
......@@ -219,3 +239,22 @@ body::-webkit-scrollbar, html::-webkit-scrollbar {
max-width: 300px;
}
}
/* 调试面板样式 */
.debug-panel {
position: fixed;
top: 20px;
right: 20px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
padding: 15px;
border-radius: 8px;
font-size: 14px;
z-index: 100;
min-width: 200px;
}
.debug-info div {
margin-bottom: 5px;
font-family: monospace;
}
\ No newline at end of file
......@@ -27,7 +27,25 @@
</transition>
</div>
<div class="subtitle-bar" v-if="currentAudioText">
<div class="subtitle-text">{{ currentAudioText }}</div>
<div class="subtitle-text">
<span
v-for="(word, index) in currentWords"
:key="index"
:class="{ 'highlighted-word': highlightedWordIndexes.includes(index) }"
>
{{ word.word }}
</span>
</div>
</div>
<!-- 调试面板 -->
<div class="debug-panel" v-if="isPlaying">
<div class="debug-info">
<div>时间: {{ debugInfo.currentTime.toFixed(2) }}s</div>
<div>单词数: {{ debugInfo.totalWords }}</div>
<div>高亮索引: {{ debugInfo.highlightedIndexes.join(', ') }}</div>
<div>当前单词: {{ debugInfo.currentWord }}</div>
</div>
</div>
<div class="fixed-btn-group">
<el-button
......
......@@ -38,6 +38,9 @@ function initializeApp(data) {
isRectVisible: false,
pageTransition: 'slide-left',
needJumpToPageStartOnPlay: false,
// 逐字高亮相关
currentWords: [],
highlightedWordIndexes: [],
},
computed: {
currentGroups() {
......@@ -73,6 +76,17 @@ function initializeApp(data) {
width: (r.width * this.imageSize.width) + 'px',
height: (r.height * this.imageSize.height) + 'px',
};
},
// 调试信息
debugInfo() {
if (this.currentWords.length === 0) return '无单词数据';
const currentWord = this.highlightedWordIndexes.length > 0 ? this.currentWords[this.highlightedWordIndexes[0]] : null;
return {
currentTime: this.audio ? this.audio.currentTime : 0,
totalWords: this.currentWords.length,
highlightedIndexes: this.highlightedWordIndexes,
currentWord: currentWord ? `${currentWord.word} (${currentWord.start}-${currentWord.end})` : '无'
};
}
},
watch: {
......@@ -173,14 +187,54 @@ function initializeApp(data) {
// 字幕严格跟随 audio_points.points
let subtitle = '';
let currentPointIndex = -1;
let currentWords = [];
let highlightedWordIndexes = [];
for (let i = 0; i < this.audioPoints.length; i++) {
const p = this.audioPoints[i];
if (currentTime >= p.start && currentTime <= p.end) {
subtitle = p.text;
currentPointIndex = i;
currentWords = p.words || [];
// 改进的单词高亮逻辑
if (currentWords.length > 0) {
// 找到所有时间匹配的单词(允许多个单词同时高亮)
for (let j = 0; j < currentWords.length; j++) {
const word = currentWords[j];
if (currentTime >= word.start && currentTime <= word.end) {
highlightedWordIndexes.push(j);
}
}
// 如果没有精确匹配,尝试找到所有在合理时间范围内的单词
if (highlightedWordIndexes.length === 0) {
for (let j = 0; j < currentWords.length; j++) {
const word = currentWords[j];
const distanceToStart = Math.abs(currentTime - word.start);
const distanceToEnd = Math.abs(currentTime - word.end);
const minWordDistance = Math.min(distanceToStart, distanceToEnd);
// 如果距离在合理范围内(比如0.3秒内),就高亮这个单词
if (minWordDistance <= 0.3) {
highlightedWordIndexes.push(j);
}
}
}
// 调试信息
const highlightedWords = highlightedWordIndexes.map(idx => currentWords[idx]?.word).join(', ');
console.log('当前时间:', currentTime, '高亮单词索引:', highlightedWordIndexes, '单词:', highlightedWords);
}
break;
}
}
this.currentAudioText = subtitle;
this.currentAudioPointIndex = currentPointIndex;
this.currentWords = currentWords;
this.highlightedWordIndexes = highlightedWordIndexes;
},
handleAudioEnded() {
this.isPlaying = false;
......@@ -188,6 +242,10 @@ function initializeApp(data) {
this.isRectVisible = false;
this.activeGroupIndexes = [];
this.currentPage = 0; // 播放结束后回到第一页
// 重置逐字高亮状态
this.currentWords = [];
this.highlightedWordIndexes = [];
this.currentAudioPointIndex = -1;
},
togglePlay() {
if (this.isPlaying) {
......
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