From fa41955eb7ced6f10c4cd748b7b1193dc9526ad3 Mon Sep 17 00:00:00 2001 From: ikmkj <1@qq,com> Date: Sat, 26 Jul 2025 02:06:13 +0800 Subject: [PATCH] =?UTF-8?q?fix(music-player):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E6=AD=8C=E8=AF=8D=E8=A7=A3=E6=9E=90=E5=92=8C=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 更新歌词解析正则,支持更广泛的歌词格式 - 修复歌词时间计算,精确到毫秒 -优化无歌词时的处理逻辑,显示每行文本 - 改进歌词滚动逻辑,确保正确显示当前行 - 增加对 currentTime 的监听,实时更新歌词显示 --- music-qianduan/src/components/MusicPlayer.vue | 77 +++++++++++++++---- 1 file changed, 61 insertions(+), 16 deletions(-) diff --git a/music-qianduan/src/components/MusicPlayer.vue b/music-qianduan/src/components/MusicPlayer.vue index 0656e4e..d2bbc6b 100644 --- a/music-qianduan/src/components/MusicPlayer.vue +++ b/music-qianduan/src/components/MusicPlayer.vue @@ -386,22 +386,29 @@ const parsedLyric = computed(() => { const lyric = currentMusic.value.lyric; const lines = lyric.split('\n'); const result = []; - const timeRegExp = /\[(\d{2}):(\d{2})\.(\d+)\]/g; + const timeRegExp = /\[(\d{2}):(\d{2})\.(\d{2,3})\]/g; for (const line of lines) { const text = line.replace(timeRegExp, '').trim(); - if (text) { - timeRegExp.lastIndex = 0; - let match; - while ((match = timeRegExp.exec(line)) !== null) { - const min = parseInt(match, 10); - const sec = parseInt(match, 10); - const msStr = match; - const time = min * 60 + sec + parseFloat('0.' + msStr); + timeRegExp.lastIndex = 0; + let match; + while ((match = timeRegExp.exec(line)) !== null) { + const min = parseInt(match[1], 10); + const sec = parseInt(match[2], 10); + const msPart = match[3]; + const msStr = msPart.padEnd(3, '0'); + const ms = parseInt(msStr, 10); + const time = min * 60 + sec + ms / 1000; + if (text) { result.push({ time, text }); } } } + + if (result.length === 0) { + return lines.map(line => ({ time: 0, text: line.trim() })); + } + return result.sort((a, b) => a.time - b.time); }) @@ -523,18 +530,28 @@ const formatSliderTooltip = (val) => formatTime(val) // 监听 Pinia store 的时间更新 watch(currentTime, (newVal) => { if (!isUserSeeking.value) { - sliderTime.value = newVal + sliderTime.value = newVal; } // 更新歌词 - if (parsedLyric.value.length > 0) { - let newIndex = parsedLyric.value.findIndex(line => line.time > newVal) - 1 - if (newIndex < 0) newIndex = parsedLyric.value.length - 1 + if (parsedLyric.value && parsedLyric.value.length > 0) { + let newIndex = parsedLyric.value.findIndex(l => l.time > newVal); + + if (newIndex === -1) { + // We are at or after the last lyric line + newIndex = parsedLyric.value.length - 1; + } else { + // The active line is the one before the next line + newIndex = newIndex - 1; + } + if (currentLyricIndex.value !== newIndex) { - currentLyricIndex.value = newIndex - if (isExpanded.value) scrollToActiveLyric() + currentLyricIndex.value = newIndex; + if (isExpanded.value && newIndex >= 0) { + scrollToActiveLyric(); + } } } -}) +}); // 滚动到当前歌词 const scrollToActiveLyric = async () => { @@ -577,6 +594,34 @@ watch(() => currentMusic.value?.id, (newId) => { if (isExpanded.value) expandedActiveTab.value = 'lyric' } }) + +// 监听当前播放时间,更新歌词 +watch(currentTime, (newTime) => { + if (!parsedLyric.value || parsedLyric.value.length === 0) return + + let newIndex = -1 + for (let i = 0; i < parsedLyric.value.length; i++) { + if (newTime >= parsedLyric.value[i].time) { + newIndex = i + } else { + break + } + } + + if (newIndex !== currentLyricIndex.value) { + currentLyricIndex.value = newIndex + if (newIndex > -1 && lyricScrollbar.value && lyricLines.value[newIndex]) { + const lineEl = lyricLines.value[newIndex] + const scrollbarEl = lyricScrollbar.value.wrapRef + if (scrollbarEl) { + const offsetTop = lineEl.offsetTop + const scrollbarHeight = scrollbarEl.clientHeight + const targetScrollTop = offsetTop - scrollbarHeight / 2 + lineEl.clientHeight / 2 + lyricScrollbar.value.setScrollTop(targetScrollTop) + } + } + } +})