Browse Source

fix(音频控制): 优化页面切换时的高亮和滚动响应

- 修改自动播放逻辑,立即更新高亮索引并触发滚动事件
- 增强emitHighlightChange和emitScrollToText方法,支持传入音频数据
- 改进滚动检测逻辑,降低敏感度并记录最后滚动时间
- 实现精确滚动位置计算,减少不必要的滚动操作
- 优化防抖延迟时间,提高用户交互响应性
main
前端-胡立永 1 day ago
parent
commit
66697b21ca
2 changed files with 232 additions and 31 deletions
  1. +31
    -20
      subPages/home/AudioControls.vue
  2. +201
    -11
      subPages/home/book.vue

+ 31
- 20
subPages/home/AudioControls.vue View File

@ -321,7 +321,18 @@ export default {
console.log(`🎵 自动播放缓存音频: ${firstAudioData.url}`); console.log(`🎵 自动播放缓存音频: ${firstAudioData.url}`);
audioManager.playAudio(firstAudioData.url, 'sentence', { playbackRate: this.playSpeed }); audioManager.playAudio(firstAudioData.url, 'sentence', { playbackRate: this.playSpeed });
this.isPlaying = true; this.isPlaying = true;
this.updateHighlightIndex();
//
const highlightIndex = firstAudioData.originalTextIndex !== undefined ? firstAudioData.originalTextIndex : 0;
this.currentHighlightIndex = highlightIndex;
//
this.emitHighlightChange(highlightIndex, firstAudioData);
//
this.emitScrollToText(highlightIndex, firstAudioData);
console.log(`🎵 页面切换自动播放: 高亮索引=${highlightIndex}, 页面=${this.currentPage}`);
} }
}); });
} else { } else {
@ -1560,27 +1571,27 @@ export default {
}, },
// //
emitHighlightChange(highlightIndex = -1) {
emitHighlightChange(highlightIndex = -1, audioData = null) {
if (highlightIndex === -1) { if (highlightIndex === -1) {
// //
this.$emit('highlight-change', -1); this.$emit('highlight-change', -1);
return; return;
} }
// 使currentAudioIndexhighlightIndex
const audioData = this.currentPageAudios[this.currentAudioIndex];
if (!audioData) {
// 使audioData
const currentAudioData = audioData || this.currentPageAudios[this.currentAudioIndex];
if (!currentAudioData) {
this.$emit('highlight-change', -1); this.$emit('highlight-change', -1);
return; return;
} }
const highlightData = { const highlightData = {
highlightIndex: audioData.originalTextIndex !== undefined ? audioData.originalTextIndex : highlightIndex,
isSegmented: audioData.isSegmented || false,
segmentIndex: audioData.segmentIndex || 0,
startIndex: audioData.startIndex || 0,
endIndex: audioData.endIndex || 0,
currentText: audioData.text || ''
highlightIndex: currentAudioData.originalTextIndex !== undefined ? currentAudioData.originalTextIndex : highlightIndex,
isSegmented: currentAudioData.isSegmented || false,
segmentIndex: currentAudioData.segmentIndex || 0,
startIndex: currentAudioData.startIndex || 0,
endIndex: currentAudioData.endIndex || 0,
currentText: currentAudioData.text || ''
}; };
// //
@ -1588,14 +1599,14 @@ export default {
}, },
// //
emitScrollToText(highlightIndex = -1) {
emitScrollToText(highlightIndex = -1, audioData = null) {
if (highlightIndex === -1) { if (highlightIndex === -1) {
return; return;
} }
//
const audioData = this.currentPageAudios[this.currentAudioIndex];
if (!audioData) {
// 使audioData
const currentAudioData = audioData || this.currentPageAudios[this.currentAudioIndex];
if (!currentAudioData) {
return; return;
} }
@ -1604,16 +1615,16 @@ export default {
const currentPageCache = this.audioCache[audioCacheKey]; const currentPageCache = this.audioCache[audioCacheKey];
// //
if (!currentPageCache || !currentPageCache.audios.includes(audioData)) {
if (!currentPageCache || !currentPageCache.audios.includes(currentAudioData)) {
console.warn('🎵 emitScrollToText: 音频数据与当前页面不匹配,跳过滚动事件'); console.warn('🎵 emitScrollToText: 音频数据与当前页面不匹配,跳过滚动事件');
return; return;
} }
const scrollData = { const scrollData = {
highlightIndex: audioData.originalTextIndex !== undefined ? audioData.originalTextIndex : highlightIndex,
isSegmented: audioData.isSegmented || false,
segmentIndex: audioData.segmentIndex || 0,
currentText: audioData.text || '',
highlightIndex: currentAudioData.originalTextIndex !== undefined ? currentAudioData.originalTextIndex : highlightIndex,
isSegmented: currentAudioData.isSegmented || false,
segmentIndex: currentAudioData.segmentIndex || 0,
currentText: currentAudioData.text || '',
currentPage: this.currentPage currentPage: this.currentPage
}; };


+ 201
- 11
subPages/home/book.vue View File

@ -191,6 +191,7 @@ export default {
touchStartTime: 0, // touchStartTime: 0, //
touchStartY: 0, // Y touchStartY: 0, // Y
userScrollTimer: null, // userScrollTimer: null, //
lastUserScrollTime: 0, //
courseIdList: [], courseIdList: [],
bookTitle: '', bookTitle: '',
courseList: [ courseList: [
@ -322,6 +323,9 @@ export default {
// //
if (deltaY > 10) { if (deltaY > 10) {
//
this.lastUserScrollTime = Date.now();
// //
if (this.isScrolling) { if (this.isScrolling) {
console.log('🛑 检测到用户手动滚动,停止自动滚动'); console.log('🛑 检测到用户手动滚动,停止自动滚动');
@ -345,14 +349,17 @@ export default {
this.userScrollTimer = setTimeout(() => { this.userScrollTimer = setTimeout(() => {
console.log('✋ 用户滚动操作结束,允许自动滚动'); console.log('✋ 用户滚动操作结束,允许自动滚动');
this.userScrollTimer = null; this.userScrollTimer = null;
}, 1000); // 1
}, 500); // 500ms
console.log('👆 用户停止触摸屏幕'); console.log('👆 用户停止触摸屏幕');
}, },
// //
shouldPreventAutoScroll() { shouldPreventAutoScroll() {
return this.isUserTouching || this.userScrollTimer !== null;
//
const now = Date.now();
const recentUserScroll = this.userScrollTimer !== null && (now - this.lastUserScrollTime) < 1000;
return this.isUserTouching && recentUserScroll;
}, },
// scroll-view // scroll-view
@ -368,9 +375,10 @@ export default {
if (this.isScrolling) { if (this.isScrolling) {
// //
const scrollDifference = Math.abs(previousScrollTop - scrollTop); const scrollDifference = Math.abs(previousScrollTop - scrollTop);
if (scrollDifference > 100) { //
if (scrollDifference > 50) { //
console.log('🖐️ 检测到手动滚动,中断自动滚动状态'); console.log('🖐️ 检测到手动滚动,中断自动滚动状态');
this.isScrolling = false; this.isScrolling = false;
this.lastUserScrollTime = Date.now(); //
} }
} }
@ -728,6 +736,8 @@ export default {
// //
onScrollToText(scrollData) { onScrollToText(scrollData) {
console.log('📍 收到滚动请求:', scrollData);
// //
if (this.shouldPreventAutoScroll()) { if (this.shouldPreventAutoScroll()) {
console.log('🚫 用户正在手动滚动,跳过自动滚动到文本'); console.log('🚫 用户正在手动滚动,跳过自动滚动到文本');
@ -746,7 +756,7 @@ export default {
return; return;
} }
this.performScrollToText(scrollData); this.performScrollToText(scrollData);
}, 100); // 100ms
}, 50); //
}, },
// //
@ -817,7 +827,52 @@ export default {
this.isScrolling = true; this.isScrolling = true;
// DOM // DOM
this.$nextTick(() => {
this.$nextTick(async () => {
try {
//
const elementPositions = await this.getAllElementPositions();
console.log('📏 获取到的元素位置信息:', elementPositions);
//
const preciseScrollTop = await this.calculatePreciseScrollPosition(scrollData, elementPositions);
if (preciseScrollTop !== null) {
//
const currentScroll = this.scrollTops[this.currentPage - 1] || 0;
const scrollDifference = Math.abs(preciseScrollTop - currentScroll);
if (scrollDifference > 20) {
this.$set(this.scrollTops, this.currentPage - 1, preciseScrollTop);
console.log('✅ 使用精确位置滚动:', {
selector,
preciseScrollTop,
currentPage: this.currentPage,
scrollDifference
});
//
setTimeout(() => {
resetScrollingState();
}, 200);
} else {
resetScrollingState();
console.log('📍 目标已在视野内,无需滚动');
}
} else {
// 使
console.log('🔄 精确计算失败,使用备用查询方法');
this.fallbackScrollToText(selector, resetScrollingState, safetyTimeout);
}
} catch (error) {
console.error('❌ 精确滚动计算失败:', error);
// 使
this.fallbackScrollToText(selector, resetScrollingState, safetyTimeout);
}
});
},
//
fallbackScrollToText(selector, resetScrollingState, safetyTimeout) {
// 使uni.createSelectorQuery // 使uni.createSelectorQuery
const query = uni.createSelectorQuery().in(this); const query = uni.createSelectorQuery().in(this);
@ -855,13 +910,13 @@ export default {
const currentScroll = this.scrollTops[this.currentPage - 1] || 0; const currentScroll = this.scrollTops[this.currentPage - 1] || 0;
const scrollDifference = Math.abs(finalScrollTop - currentScroll); const scrollDifference = Math.abs(finalScrollTop - currentScroll);
if (scrollDifference > 30) { //
if (scrollDifference > 20) { //
this.$set(this.scrollTops, this.currentPage - 1, finalScrollTop); this.$set(this.scrollTops, this.currentPage - 1, finalScrollTop);
// //
setTimeout(() => { setTimeout(() => {
resetScrollingState(); resetScrollingState();
}, 300); //
}, 200); //
console.log('✅ 滚动到高亮文本:', { console.log('✅ 滚动到高亮文本:', {
selector, selector,
@ -887,18 +942,18 @@ export default {
// //
if (!targetRect) { if (!targetRect) {
console.log('🔄 尝试备用滚动方案'); console.log('🔄 尝试备用滚动方案');
const fallbackScrollTop = scrollData.highlightIndex * 100; //
this.$set(this.scrollTops, this.currentPage - 1, fallbackScrollTop);
// highlightIndex
const estimatedPosition = this.calculateEstimatedScrollPosition(scrollData.highlightIndex);
this.$set(this.scrollTops, this.currentPage - 1, estimatedPosition);
setTimeout(() => { setTimeout(() => {
resetScrollingState(); resetScrollingState();
}, 300);
}, 200);
} else { } else {
// //
resetScrollingState(); resetScrollingState();
} }
} }
}); });
});
}, },
// //
@ -922,6 +977,141 @@ export default {
return -1; // return -1; //
}, },
//
async getAllElementPositions() {
return new Promise((resolve) => {
const currentPageData = this.bookPages[this.currentPage - 1];
if (!currentPageData || !Array.isArray(currentPageData)) {
resolve([]);
return;
}
const query = uni.createSelectorQuery().in(this);
const elementPositions = [];
// scroll-container
query.select('.scroll-container').boundingClientRect();
//
currentPageData.forEach((item, index) => {
if (item && (item.type === 'text' || item.type === 'image' || item.type === 'video')) {
if (item.type === 'text') {
query.select(`#text-${index}`).boundingClientRect();
} else if (item.type === 'image') {
query.select(`.image-container`).boundingClientRect();
} else if (item.type === 'video') {
query.select(`.video-content`).boundingClientRect();
}
}
});
query.exec((res) => {
const containerRect = res[0];
if (!containerRect) {
resolve([]);
return;
}
//
let resultIndex = 1; //
currentPageData.forEach((item, index) => {
if (item && (item.type === 'text' || item.type === 'image' || item.type === 'video')) {
const elementRect = res[resultIndex];
if (elementRect) {
elementPositions.push({
index: index,
type: item.type,
top: elementRect.top - containerRect.top,
height: elementRect.height,
bottom: elementRect.top - containerRect.top + elementRect.height
});
}
resultIndex++;
}
});
resolve(elementPositions);
});
});
},
//
async calculatePreciseScrollPosition(scrollData, elementPositions) {
if (!elementPositions || elementPositions.length === 0) {
return null;
}
let targetElementIndex = -1;
if (scrollData.segmentIndex !== undefined) {
//
targetElementIndex = scrollData.segmentIndex;
} else if (scrollData.highlightIndex !== undefined) {
//
targetElementIndex = this.findTextItemIndex(scrollData.highlightIndex);
}
if (targetElementIndex === -1) {
return null;
}
//
const targetElement = elementPositions.find(pos => pos.index === targetElementIndex && pos.type === 'text');
if (!targetElement) {
console.warn('未找到目标元素位置信息:', targetElementIndex);
return null;
}
// 1/4
const screenHeight = uni.getSystemInfoSync().windowHeight;
const offsetFromTop = screenHeight * 0.25;
// = -
const targetScrollTop = Math.max(0, targetElement.top - offsetFromTop);
console.log('🎯 精确滚动位置计算:', {
targetElementIndex,
targetElement,
screenHeight,
offsetFromTop,
targetScrollTop
});
return targetScrollTop;
},
//
calculateEstimatedScrollPosition(highlightIndex) {
const currentPageData = this.bookPages[this.currentPage - 1];
if (!currentPageData || !Array.isArray(currentPageData)) {
return highlightIndex * 80; //
}
//
let estimatedHeight = 0;
let textCount = 0;
for (let i = 0; i < currentPageData.length && textCount <= highlightIndex; i++) {
const item = currentPageData[i];
if (item && item.type === 'text' && item.content) {
if (textCount === highlightIndex) {
break;
}
//
const contentLength = item.content.length;
estimatedHeight += Math.max(60, contentLength * 1.2); // +
textCount++;
} else if (item && item.type === 'image') {
estimatedHeight += 200; //
} else if (item && item.type === 'video') {
estimatedHeight += 300; //
}
}
return Math.max(0, estimatedHeight - 100); //
},
// id // id
async getVoiceList() { async getVoiceList() {
const voiceRes = await this.$api.music.list() const voiceRes = await this.$api.music.list()


Loading…
Cancel
Save