大版本更新
一口气全面统一博客样式,移除部分冗余导航页面,更专注内容展示。
不因虚度年华而悔恨,不因碌碌无为而羞愧
博客系列第12期。为博客音乐播放器接入歌词:LRC 解析工具、内容仓歌词资源管理、面板歌词 / 悬浮歌词条 / 胶囊歌词三处消费,以及 Meting 在线平台的歌词透传。

接入完成后,歌词在三个地方消费同一份数据:
三处共用一个解析结果和一个「当前行」计算,播放逻辑零侵入。
LRC 本质是「时间标签 + 文本」的行式格式。解析器只做一件事:把 [mm:ss.xx] 抽出来换算成秒并排序。
1export interface LrcLine {2 time: number; // 秒3 text: string;4}5
6export function parseLrc(raw: string): LrcLine[] {7 const lines: LrcLine[] = [];8 for (const line of raw.split(/\r?\n/)) {9 // 一行可能有多个时间标签(合并重复句),全部展开10 const times = [...line.matchAll(/\[(\d+):(\d+)(?:\.(\d+))?\]/g)];11 if (times.length === 0) continue;12 const text = line.replace(/\[(\d+):(\d+)(?:\.(\d+))?\]/g, "").trim();13 for (const m of times) {14 const time = Number(m[1]) * 60 + Number(m[2]) + Number(`0.${m[3] ?? "0"}`);15 lines.push({ time, text });16 }17 }18 return lines.sort((a, b) => a.time - b.time);19}「当前行」用一次线性扫描就够了——歌词通常不超过百行,没必要二分:
1export function currentLrcIndexAt(lines: LrcLine[], t: number): number {2 let index = -1;3 for (let i = 0; i < lines.length; i++) {4 if (lines[i].time <= t) index = i;5 else break;6 }7 return index;8}歌词文件放在内容仓 public/assets/music/lrc/ 下,与曲目在 data/music.ts 里对接:
1export const musicTracks = [2 {3 id: "koufue",4 title: "口笛で愛は歌えない",5 artist: "Dazbee",6 cover: "/assets/music/cover/dazbee.webp",7 src: "/assets/music/audio/koufue.mp3",8 // 歌词:远端 .lrc 地址或内嵌 LRC 文本均可9 lrc: "/assets/music/lrc/koufue.lrc",10 },11 // ...12];歌词内容的获取是懒加载的:面板或歌词条第一次需要显示时才 fetch,解析结果按曲目缓存——不听歌的访客不会为歌词付出任何请求。
本地曲目之外,Meting(在线音乐平台聚合)模式同样支持歌词:接口返回的 lrc 字段原样透传给同一个解析器。这样无论曲目来自本地还是在线平台,下游三处消费逻辑完全一致。
1// Meting 曲目解析时保留 lrc 字段2if (metingLrc) {3 track.lrc = metingLrc; // 透传,下游统一懒加载解析4}| 消费位置 | 交互 | 备注 |
|---|---|---|
| 歌词面板 | 自动滚动跟随 + 点击跳转 + 手动滚动暂停 3s | 功能最全 |
| 悬浮歌词条 | 只显示当前行 | 常驻屏幕下缘,无滚动 |
| 胶囊歌词 | 只显示当前行,超长省略 | 嵌在悬浮控件里 |
自动滚动跟随的实现要点:歌词行变化时 scrollTo 目标行到容器垂直居中;用户手动滚动(wheel / touchstart)后暂停跟随 3 秒再恢复,避免「人在看上一句、视图被拽走」的对抗。
无 lrc 配置 → 显示「暂无歌词」占位 → 相关按钮隐藏。歌词加载失败同理,占位文案区分「加载中」和「没有歌词」,不留空白区域。
整个歌词功能约 200 行:一个解析器、一个当前行计算、三处消费、一次懒加载。配置上 showLyrics: false 一行可以让全部歌词 UI 零 DOM;每首歌想不加歌词,不写 lrc 字段就行。
生成精美分享图或复制链接,与更多人分享本文。
最后更新于 ,距今已过 39 天
部分内容可能已过时