//歌词组件LyricsDisplay.vue
<template>
<div class="lyric-container" ref="lyricsList" :class="{ noLyric: !lyrics.length }">
<div v-for="(line, index) in lyrics" :key="index" ref="lyricLine" :class="{ active: index === currentIndex }">
{{ line.text }}
</div>
<div class="empty-lyric" v-if="!lyrics.length">
当前歌曲无歌词
</div>
</div>
</template>
<script>
export default {
props: {
lyrics: { type: Array, required: true }, // 已经解析好的歌词对象数组
currentTime: { type: Number, required: true }, // 当前播放时间(秒)
},
computed: {
currentIndex () {
const currentIndexInRange = this.lyrics.findIndex((item, index) => {
if (index < this.lyrics.length - 1) { // 防止数组越界
return item.time <= this.currentTime && this.lyrics[index + 1].time > this.currentTime;
}
return false; // 如果已经是最后一项,返回false,后续不会再检查
});
// 如果没有找到符合条件的歌词,返回-1或其他默认值
return currentIndexInRange !== -1 ? currentIndexInRange : -1;
},
},
watch: {
currentIndex (newVal) {
this.$nextTick(() => {
if (newVal > -1) {
// 获取当前歌词项对应的DOM元素
const currentLyricElement = this.$refs.lyricLine[newVal];
// 计算容器高度和当前元素高度
const containerHeight = this.$refs.lyricsList.clientHeight;
const elementHeight = currentLyricElement.clientHeight;
// 计算需要滚动到的偏移量,使元素位于容器中间
const scrollTop = currentLyricElement.offsetTop - this.$refs.lyricsList.offsetTop - (containerHeight / 2 - elementHeight / 2);
// 平滑滚动到计算出的偏移量
this.$refs.lyricsList.scrollTop = scrollTop;
}
});
},
},
};
</script>
<style lang="scss" scoped>
.lyric-container {
overflow-y: scroll;
width: 80%;
height: 230px;
margin: 0 auto;
color: rgba(255, 255, 255, 0.3);
font-size: 14px;
line-height: 34px;
text-align: center;
.empty-lyric {
font-size: 16px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
.noLyric {
position: relative;
}
.lyric-container::-webkit-scrollbar {
display: none;
}
.active {
color: #ffffff;
font-size: 16px;
}
</style>
//解析lrc歌词lyric-parser.js
export function parseLyric(lrcContent) {
return lrcContent.split('\n').reduce((lyrics, line) => {
const timeMatch = /\[(\d{2}:\d{2}(\.\d{2,3})?)\]/.exec(line);
if (timeMatch) {
const timeStr = timeMatch[1];
const parts = timeStr.split(':').map(parseFloat);
const time = parts[0] * 60 + (parts[1] + (parts[2] || 0) / 1000);
const lyric = line.replace(timeMatch[0], '').trim();
lyrics.push({ time, text: lyric });
}
return lyrics;
}, []);
}
使用:
<template>
<div>
<audio ref="audio" @timeupdate="updateTime"></audio>
<lyrics-display :lyrics="parsedLyrics" :currentTime="currentTime"></lyrics-display>
</div>
</template>
<script>
import LyricsDisplay from '@/components/LyricsDisplay.vue';
import { parseLyric } from '@/utils/lyric-parser.js';
export default {
components: {
LyricsDisplay,
},
data () {
return {
currentTime: 0,
parsedLyrics: [],
};
},
methods: {
updateTime (e) {
this.currentTime = e.target.currentTime;
},
async function fetchAndParseLyric(url) {
try {
// 异步请求歌词文件
const response = await fetch(url);
const lrcContent = await response.text();
// 使用parseLyric函数解析歌词
const parsedLyrics = parseLyric(lrcContent);
// 返回解析后的歌词对象数组
return parsedLyrics;
} catch (error) {
console.error('Failed to fetch or parse lyric:', error);
return null;
}
},
},
mounted(){
// 调用函数解析歌词
this.fetchAndParseLyric('https://axxx.txt')
.then(parsedLyrics => {
console.log(parsedLyrics);
// 处理解析后的歌词数据
this.parsedLyrics=parsedLyrics
});
}
}