Hugo LoveIt 主题:APlayer + PJAX 实现页面切换音乐不中断
目录
引言
搭建个人博客后,一直想在左下角放个音乐播放器。切换页面时音乐不能断——这是最基本的要求。市面上大部分 Hugo 音乐播放器教程都针对 Stack 主题,LoveIt 的资料很少。折腾了好几天终于搞定了,记录一下。
最终效果
- 🎵 APlayer 完整 UI:封面 + 歌名/歌手 + 进度条 + 时间 + 音量 + 循环模式 + 歌单
- 🔀 PJAX 无刷新加载,切换页面音乐零中断
- 💾 localStorage 保进度,F5 刷新也能恢复
- 📝
hugo.toml配置歌单,加歌不改代码
核心技术
PJAX 拦截内部链接,用 AJAX 只替换变化的部分(.main .container)。APlayer 放在 .wrapper 外,永远不会被销毁。再配合 localStorage 保留播放进度,F5 后也能恢复。
文件改动
| 文件 | 作用 |
|---|---|
layouts/baseof.html | 覆盖主题模板,加 APlayer + PJAX |
assets/js/theme.js | 暴露 Theme 实例,加 reinitContent() |
hugo.toml | [[params.musicList]] 歌单配置 |
static/music/ | 存放 mp3 和封面 jpg |
实现细节
1. baseof.html
从 themes/LoveIt/layouts/baseof.html 复制到项目同名路径,Hugo 会自动覆盖。
APlayer(放在 .wrapper 外):
listFolded: true + 初始化后 JS 自动点击展开 → 切换到 aplayer-withlist 模式,全 UI 可见。
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css">
<div id="aplayer"></div>
<script src="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js"></script>
<script>
var ap = new APlayer({
container: document.getElementById('aplayer'),
fixed: true,
theme: '#448aff',
loop: 'all', order: 'list',
volume: 0.7, mutex: true,
lrcType: 0,
listFolded: true,
listMaxHeight: '250px',
audio: [
{{ range $index, $song := .Site.Params.musicList }}
{ name: '{{ $song.name }}', artist: '{{ $song.artist }}',
url: '{{ $song.url }}', cover: '{{ $song.cover }}' },
{{ end }}
]
});
// 自动展开切换到完整 UI 模式 + 自动打开歌单
setTimeout(function() {
var btn = document.querySelector('#aplayer .aplayer-miniswitcher');
if (btn) btn.click();
setTimeout(function() {
var listBtn = document.querySelector('#aplayer .aplayer-icon-menu');
if (listBtn) listBtn.click();
}, 200);
}, 500);
// 播放进度保留
window.onbeforeunload = function() {
localStorage.setItem('playInfo', JSON.stringify({
index: ap.list.index, currentTime: ap.audio.currentTime, paused: ap.paused
}));
};
window.onload = function() {
var saved = JSON.parse(localStorage.getItem('playInfo'));
if (!saved) return;
ap.list.switch(saved.index);
setTimeout(function() { ap.seek(saved.currentTime); if (!saved.paused) ap.play(); }, 500);
};
</script>少量 CSS 隐藏歌词、适配深色模式歌单背景:
<style>
#aplayer .aplayer-lrc { display:none!important; }
[theme="dark"] #aplayer.aplayer-fixed .aplayer-list { background:#2e2e2e; }
[theme="dark"] #aplayer.aplayer-fixed .aplayer-list ol li { color:#ddd; border-top-color:rgba(255,255,255,0.07); }
</style>PJAX(放在 </body> 前):
<script src="https://cdn.jsdelivr.net/npm/pjax/pjax.min.js"></script>
<script>
var pjax = new Pjax({ selectors: [".main .container"] });
pjax._handleResponse = pjax.handleResponse;
pjax.handleResponse = function(responseText, request, href, options) {
if (request.responseText.match("<html")) {
if (responseText) {
let newDom = new DOMParser().parseFromString(responseText, 'text/html');
document.body.setAttribute("class", newDom.body.className);
document.title = newDom.title;
pjax._handleResponse(responseText, request, href, options);
}
}
};
document.addEventListener('pjax:complete', function() {
window.reinitContent();
});
</script>2. theme.js
从 themes/LoveIt/assets/js/theme.js 复制到 assets/js/theme.js,改末尾两处:
const themeInit = () => {
window.__theme = new Theme();
window.__theme.init();
};
window.reinitContent = () => {
if (window.__theme) {
window.__theme.initRaw(); window.__theme.initSVGIcon();
window.__theme.initHighlight(); window.__theme.initMath();
window.__theme.initMermaid(); window.__theme.initHeaderLink();
window.__theme.initDetails(); window.__theme.initLightGallery();
window.__theme.initEcharts(); window.__theme.initMapbox();
window.__theme.initTypeit(); window.__theme.initTwemoji();
window.__theme.initCookieconsent();
window.setTimeout(() => {
window.__theme.initToc(); window.__theme.initComment();
}, 100);
}
};菜单、搜索、主题切换在 header 里,PJAX 不替换 header,这些不需要重初始化。
3. hugo.toml
[[params.musicList]]
name = "Coffee"
artist = "beabadoobee"
url = "/music/Coffee.mp3"
cover = "/music/Coffee.jpg"
[[params.musicList]]
name = "怪天气"
artist = "YELLOW黃宣 / 9m88"
url = "/music/怪天气.mp3"
cover = "/music/怪天气.jpg"每首歌一个 [[params.musicList]] 块,加歌只改这一个文件。音乐文件放在 static/music/。
窄模式陷阱
listFolded: false 会触发窄模式——info 区(进度条、时间、音量)的 DOM 根本不创建,后续改 CSS 没用。必须用 listFolded: true + JS 点击展开。
PJAX 已知局限
| 问题 | 状态 |
|---|---|
| 切页后代码高亮丢失 | ✅ 已修复 |
| 切页后数学公式不渲染 | ✅ 已修复 |
| 切页后 TOC 消失 | ✅ 已修复 |
| 搜索跳转刷新页面 | ⚠️ 未完全解决 |
总结
- PJAX 只替换
.main .container——不动 header 和 footer - APlayer 在
.wrapper外——永远不被销毁 listFolded: true+ JS 展开——利用原生机制,不跟 CSS 打架hugo.toml配歌单——模板语法读入,永远不改代码
感谢 莱特雷-letere 的博客 的思路。
评论 (0)