document.addEventListener(‘DOMContentLoaded’, async function () {
// 获取音频播放器元素
const audio = document.getElementById(‘audio-player’);
// 获取歌词容器元素
const lyricsContainer = document.getElementById(‘lyrics’);
// 获取歌词容器的父容器
const container = document.getElementById(‘lyrics-container’);
// 歌词文件的URL
const lyricsFile = ‘https://fanym.oss-cn-beijing.aliyuncs.com/wp-content/uploads/2024/11/%E8%96%9B%E4%B9%8B%E8%B0%A6%20_%20%E9%BB%84%E9%BE%84%20-%20%E6%9D%A5%E6%97%A5%E6%96%B9%E9%95%BF.lrc’;
// 当前高亮的歌词索引
let currentIndex = 0;
// 存储解析后的歌词数据
let lyrics = [];
// 用户是否正在滚动歌词容器
let isUserScrolling = false;
// 用于清除定时器
let scrollTimeout;
// 从指定URL获取歌词文件内容
const lrcText = await fetchLyrics(lyricsFile);
// 解析歌词文本
parseLRC(lrcText);
function fetchLyrics(url) {
return fetch(url).then(response => response.text());
}
function parseLRC(lrcText) {
// 将歌词文本按行分割
const lines = lrcText.split(‘\n’);
// 解析每一行歌词的时间戳和文本
lyrics = lines.map(line => {
const timeMatch = line.match(/\[(\d{2}):(\d{2})\.(\d{1,2})\]/);
if (timeMatch) {
const minutes = parseInt(timeMatch[1], 10);
const seconds = parseInt(timeMatch[2], 10);
const milliseconds = parseInt(timeMatch[3].padEnd(2, ‘0’), 10);
const timeInSeconds = minutes * 60 + seconds + milliseconds / 100;
const lyricText = line.replace(/\[.*?\]/g, ”).trim();
if (lyricText) return { time: timeInSeconds, text: lyricText };
}
return null;
}).filter(item => item !== null);
// 渲染解析后的歌词
renderLyrics();
}
function renderLyrics() {
// 创建文档片段以批量插入DOM节点
const fragment = document.createDocumentFragment();
// 遍历歌词数组创建DOM节点
lyrics.forEach((lyric, index) => {
const li = document.createElement(‘li’);
li.textContent = lyric.text;
if (lyric.time !== null) {
li.dataset.time = lyric.time.toString();
// 绑定点击事件以跳转到指定时间点
li.addEventListener(‘click’, (event) => {
const clickedLi = event.target;
const clickedTime = parseFloat(clickedLi.dataset.time);
audio.currentTime = clickedTime;
const clickedIndex = Array.from(document.querySelectorAll(‘#lyrics li’)).indexOf(clickedLi);
highlightLyric(clickedLi, clickedIndex);
scrollToLyric(clickedIndex);
// 如果当前点击行的时间戳与下一行相同,则认为下一行是翻译
if (clickedIndex {
if (lyric.time && lyric.time = 0 && !isUserScrolling) {
currentIndex = foundIndex;
// 高亮当前行并滚动到该行
highlightLyric(lines[currentIndex], currentIndex);
scrollToLyric(currentIndex);
}
}
audio.addEventListener(‘timeupdate’, function () {
// 在音频播放时更新当前时间对应的歌词显示
const currentTime = audio.currentTime;
synchronizeLyricsWithAudio(currentTime);
});
function highlightLyric(line, index) {
// 移除所有行的高亮样式
document.querySelectorAll(‘#lyrics li’).forEach(li => {
li.classList.remove(‘highlight’, ‘translation’); // 移除所有高亮样式
});
// 如果当前行时间戳与前一行相同,则当前行为译文,前一行为原文
if (index > 0) {
const prevLine = document.querySelectorAll(‘#lyrics li’)[index – 1];
const prevTime = lyrics[index – 1].time;
const currentTime = lyrics[index].time;
if (prevTime === currentTime) {
prevLine.classList.add(‘highlight’); // 前一行为原文,红色高亮
line.classList.add(‘translation’); // 当前行为译文,蓝色斜体
} else {
// 如果时间戳不同,则当前行是原文
line.classList.add(‘highlight’); // 原文为红色高亮
}
} else {
// 如果是第一行,则直接高亮
line.classList.add(‘highlight’); // 原文为红色高亮
}
}
function scrollToLyric(index) {
// 获取需要滚动到的行元素
const line = document.querySelectorAll(‘#lyrics li’)[index];
const offsetTop = line.offsetTop;
const containerHeight = container.offsetHeight;
const lineHeight = line.offsetHeight;
const scrollTop = container.scrollTop;
const scrollBottom = scrollTop + containerHeight;
// 判断当前行是否已经在可视区域内
if (offsetTop scrollBottom) {
// 如果超出可视区域则滚动到该行
if (container.scrollHeight > container.offsetHeight) {
const scrollTarget = Math.max(0, offsetTop – containerHeight / 5 + lineHeight / 5);
container.scrollTop = scrollTarget;
}
}
}
container.addEventListener(‘scroll’, function () {
// 用户滚动歌词容器时设置标志位,并清除之前的定时器
isUserScrolling = true;
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(() => {
isUserScrolling = false;
}, 3000);
});
});
#lyrics li {
list-style: none;
padding: 5px;
color: #333;
cursor: pointer;
margin-bottom: 0; /* 去除行与行之间的额外空隙 */
line-height: 1.2; /* 调整行高,保持原文和译文紧密 */
}
#lyrics li.highlight {
color: #d9534f; /* 原文高亮为红色 */
font-weight: bold;
font-size: 1.2em;
background-color: rgba(255, 215, 0, 0.2); /* 原文高亮背景 */
}
#lyrics li.translation {
color: #337ab7; /* 译文为蓝色 */
font-style: italic; /* 译文使用斜体 */
margin-top: 0; /* 移除原文和译文之间的空隙 */
background-color: rgba(255, 215, 0, 0.2); /* 译文高亮背景 */
}
#audio-player {
width: 100%;
}
#lyrics-container {
/* 添加平滑滚动 */
scroll-behavior: smooth;
}