Browse Source

段落

dev_des
1054425342@qq.com 1 month ago
parent
commit
407b3114d0
  1. 216
      audioBook/pages/index.vue

216
audioBook/pages/index.vue

@ -1,27 +1,23 @@
<template>
<view class="container">
<!-- 歌词显示区域 -->
<scroll-view
class="lyrics-container"
:show-scrollbar="false"
enhanced
scroll-y
:scroll-top="scrollTop"
scroll-with-animation
:style="{ height: scrollViewHeight + 'px' }"
>
<view class="lyrics-container">
<view class="lyrics-content">
<text
v-for="(sentence, index) in currentChapter.sentences"
:key="index"
:id="`sentence-${index}`"
class="lyric-line"
:class="{ active: index === activeSentenceIndex }"
>
{{ sentence.FinalSentence }}
</text>
<!-- 当前句子显示 -->
<view class="current-sentence">
<text class="lyric-line active">
{{ currentSentence ? currentSentence.FinalSentence : "" }}
</text>
</view>
<!-- 句子指示器 -->
<view class="sentence-indicator">
<text class="sentence-text">
{{ currentSentenceIndex + 1 }}/{{ totalSentences }}
</text>
</view>
</view>
</scroll-view>
</view>
<!-- 播放控制区域 -->
<view class="player-controls">
@ -54,6 +50,29 @@
</view>
<text class="time-text">{{ formatTime(duration) }}</text>
</view>
<!-- 句子控制按钮 -->
<view class="sentence-controls">
<view
class="control-btn"
@click="prevSentence"
:disabled="currentSentenceIndex === 0"
>
<text class="control-icon-text"></text>
</view>
<view class="sentence-info">
<text class="sentence-info-text"
>{{ currentSentenceIndex + 1 }}/{{ totalSentences }}</text
>
</view>
<view
class="control-btn"
@click="nextSentence"
:disabled="currentSentenceIndex >= totalSentences - 1"
>
<text class="control-icon-text"></text>
</view>
</view>
<!-- 底部控制按钮 -->
<view class="bottom-controls">
<view class="control-btn">
@ -114,6 +133,8 @@ export default {
activeSentenceIndex: -1,
scrollTop: 0,
scrollViewHeight: 0,
//
currentSentenceIndex: 0,
};
},
mounted() {
@ -153,6 +174,13 @@ export default {
durationFormatted() {
return this.formatTime(this.duration);
},
//
totalSentences() {
return this.currentChapter.sentences.length;
},
currentSentence() {
return this.currentChapter.sentences[this.currentSentenceIndex] || null;
},
},
methods: {
togglePlay() {
@ -181,7 +209,7 @@ export default {
this.currentTime = 0;
this.duration = 0;
this.activeSentenceIndex = -1;
this.scrollTop = 0;
this.currentSentenceIndex = 0;
//
const currentChapter = this.chapters[this.currentChapterIndex];
@ -244,7 +272,7 @@ export default {
.exec();
},
updateSentenceHighlight(currentTimeSeconds) {
//
//
const sentences = this.currentChapter.sentences;
let targetIndex = -1;
@ -278,51 +306,10 @@ export default {
//
if (targetIndex >= 0 && targetIndex !== this.activeSentenceIndex) {
const previousIndex = this.activeSentenceIndex;
this.activeSentenceIndex = targetIndex;
// 使$nextTickDOM
this.$nextTick(() => {
this.scrollToSentence(targetIndex);
});
this.currentSentenceIndex = targetIndex;
}
},
scrollToSentence(index) {
//
const query = uni.createSelectorQuery().in(this);
//
query.select(`#sentence-${index}`).boundingClientRect();
//
query.select(".lyrics-content").boundingClientRect();
query.exec((res) => {
if (res && res[0] && res[1]) {
const sentenceRect = res[0];
const containerRect = res[1];
//
const sentenceTop = sentenceRect.top - containerRect.top;
//
const scrollPosition = Math.max(
0,
sentenceTop - this.scrollViewHeight / 2 + sentenceRect.height / 2
);
//
this.scrollTop = scrollPosition;
} else {
// 使
const estimatedHeight = 60;
const scrollPosition = Math.max(
0,
index * estimatedHeight - this.scrollViewHeight / 2
);
this.scrollTop = scrollPosition;
}
});
},
formatTime(seconds) {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
@ -370,6 +357,31 @@ export default {
//
this.scrollViewHeight = windowHeight - controlsHeight - 60; // 60px
},
//
prevSentence() {
if (this.currentSentenceIndex > 0) {
this.currentSentenceIndex--;
this.activeSentenceIndex = this.currentSentenceIndex;
//
const sentence =
this.currentChapter.sentences[this.currentSentenceIndex];
if (sentence && this.$refs.audioPlayer) {
this.$refs.audioPlayer.seek(sentence.startTimeInSeconds);
}
}
},
nextSentence() {
if (this.currentSentenceIndex < this.totalSentences - 1) {
this.currentSentenceIndex++;
this.activeSentenceIndex = this.currentSentenceIndex;
//
const sentence =
this.currentChapter.sentences[this.currentSentenceIndex];
if (sentence && this.$refs.audioPlayer) {
this.$refs.audioPlayer.seek(sentence.startTimeInSeconds);
}
}
},
},
};
</script>
@ -423,26 +435,41 @@ export default {
/* 歌词容器样式 */
.lyrics-container {
flex: 1;
padding: 0 20rpx;
padding: 0 60rpx;
margin-bottom: 20rpx;
width: 710rpx;
display: flex;
flex-direction: column;
justify-content: center;
}
.lyrics-content {
display: flex;
flex-direction: column;
align-items: center;
padding: 40rpx 0;
justify-content: center;
height: 100%;
position: relative;
}
.current-sentence {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.lyric-line {
display: block;
text-align: center;
font-size: 32rpx;
color: #999;
line-height: 1.6;
margin: 16rpx 0;
font-size: 36rpx;
color: #333;
line-height: 1.8;
margin: 0;
transition: all 0.3s ease;
padding: 20rpx;
font-weight: 600;
}
.lyric-line.active {
@ -451,6 +478,21 @@ export default {
font-size: 36rpx;
}
.sentence-indicator {
position: absolute;
bottom: 20rpx;
right: 20rpx;
background: rgba(0, 0, 0, 0.1);
padding: 8rpx 16rpx;
border-radius: 20rpx;
}
.sentence-text {
font-size: 24rpx;
color: #666;
font-weight: 500;
}
/* 播放控制区域 */
.player-controls {
background: white;
@ -548,6 +590,31 @@ export default {
color: #999;
}
/* 句子控制按钮 */
.sentence-controls {
display: flex;
justify-content: center;
align-items: center;
gap: 40rpx;
margin-bottom: 30rpx;
padding: 20rpx 0;
border-top: 1rpx solid #f0f0f0;
}
.sentence-info {
background: #f8f8f8;
padding: 12rpx 24rpx;
border-radius: 20rpx;
min-width: 120rpx;
text-align: center;
}
.sentence-info-text {
font-size: 28rpx;
color: #666;
font-weight: 500;
}
/* 底部控制按钮 */
.bottom-controls {
display: flex;
@ -587,4 +654,13 @@ export default {
.control-btn:active {
transform: scale(0.95);
}
.control-btn:disabled {
opacity: 0.3;
cursor: not-allowed;
}
.control-btn:disabled:active {
transform: none;
}
</style>

Loading…
Cancel
Save