目录

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 消失✅ 已修复
搜索跳转刷新页面⚠️ 未完全解决

总结

  1. PJAX 只替换 .main .container——不动 header 和 footer
  2. APlayer 在 .wrapper——永远不被销毁
  3. listFolded: true + JS 展开——利用原生机制,不跟 CSS 打架
  4. hugo.toml 配歌单——模板语法读入,永远不改代码

感谢 莱特雷-letere 的博客 的思路。

评论 (0)