2024 桐庐半程马拉松
00:00:00
时间
0.00
距离(公里)
--:--
配速
--
步频
--
心率 (bpm)
--
配速
步频
|
share-image
ESC

Hexo 博客开发笔记:打造不中断的黑胶音乐播放器

在浏览个人博客时,背景音乐能带来更好的沉浸感。但传统的多页面博客每次跳转都会刷新整个页面,导致音乐中断。本文将详细介绍如何在 Hexo 博客中实现一个黑胶风格的音乐播放器,并利用 PJAX 技术实现全站无刷新跳转,让音乐持久播放。

一、界面设计:复古黑胶风格

我们的目标是设计一个带有旋转动画和唱臂效果的迷你播放器,放置在侧边栏导航下方。

1. HTML 结构

在侧边栏模板(如 layout/partials/sidebar.ejs)中添加如下结构:

<% if (theme.music && theme.music.enable) { %>
<div class="music-player-wrapper">
<!-- 黑胶唱片主体 -->
<div class="vinyl-record-container" id="vinyl-container">
<div class="vinyl-record" id="vinyl-record">
<div class="vinyl-grooves"></div>
<div class="vinyl-label">
<div class="vinyl-hole"></div>
</div>
</div>
<!-- 唱臂 -->
<div class="tonearm" id="tonearm"></div>
</div>

<!-- 控制按钮 -->
<div class="music-controls-mini" id="music-controls">
<i class="fas fa-step-backward" id="prev-btn"></i>
<i class="fas fa-play" id="play-btn"></i>
<i class="fas fa-step-forward" id="next-btn"></i>
</div>

<!-- 音频元素 -->
<audio id="bg-music" preload="none"></audio>
</div>
<% } %>

2. CSS 样式与动画

使用 CSS3 实现唱片旋转和唱臂移动的拟物效果。核心代码如下(Stylus):

// 唱片容器
.vinyl-record-container
position relative
width 120px
height 120px
transition all 0.3s ease

// 唱片旋转动画
.vinyl-record.spinning
animation spin 4s linear infinite

@keyframes spin
from { transform: rotate(0deg) }
to { transform: rotate(360deg) }

// 唱臂设计
.tonearm
position absolute
top -10px
right -5px
width 8px
height 90px
background #ccc
transform-origin 4px 4px
transform rotate(-35deg) // 初始位置:抬起
transition transform 0.5s cubic-bezier(0.4, 0, 0.2, 1)

// 播放时的状态:唱臂移到唱片上
.vinyl-record-container.playing .tonearm
transform rotate(-5deg)

二、播放逻辑实现

通过 JavaScript 控制 audio 元素和 UI 状态的同步。

document.addEventListener('DOMContentLoaded', function() {
const audio = document.getElementById('bg-music');
const playlist = <%- JSON.stringify(theme.music.songs) %>;
let isPlaying = false;

function togglePlay() {
if (audio.paused) {
audio.play().then(() => {
isPlaying = true;
// 添加动画类
document.getElementById('vinyl-record').classList.add('spinning');
document.getElementById('vinyl-container').classList.add('playing');
// 切换图标
playBtn.classList.replace('fa-play', 'fa-pause');
});
} else {
audio.pause();
isPlaying = false;
// 移除动画类
document.getElementById('vinyl-record').classList.remove('spinning');
document.getElementById('vinyl-container').classList.remove('playing');
playBtn.classList.replace('fa-pause', 'fa-play');
}
}
});

三、核心难点:利用 PJAX 实现不中断播放

要让音乐在页面跳转时继续播放,我们需要使用 PJAX (PushState + AJAX)。它的原理是:拦截链接点击,通过 AJAX 请求新页面内容,只替换页面的主体部分(Main Content),而保留侧边栏(播放器所在位置)和页脚等不常变动的区域。

1. 引入依赖

layout.ejs 中引入 jQuery 和 jquery-pjax:

<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery.pjax/2.0.1/jquery.pjax.min.js"></script>

2. 初始化 PJAX

配置 PJAX 接管所有站内链接,并指定更新容器为 .main-content

$(document).ready(function() {
// 监听所有以根路径开头的链接
$(document).pjax('a[href^="<%= config.root %>"]', '.main-content', {
fragment: '.main-content', // 提取新页面的哪个部分
timeout: 8000
});

// PJAX 完成后的回调
$(document).on('pjax:complete', function() {
// 1. 重载代码高亮
if (window.Prism) window.Prism.highlightAll();

// 2. 重载访问量统计
$.getScript("https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js");

// 3. 其他需要重新初始化的脚本...
});
});

3. 遇到的坑与解决方案

问题一:评论区不加载
Gitalk 等评论插件通常在页面加载时初始化。PJAX 替换内容后,旧的脚本不再执行。
解决:将评论脚本放在 .main-content 容器内部,或者在 pjax:complete 回调中手动重新实例化评论插件。

问题二:第三方 JS 失效
如不蒜子统计、数学公式渲染等,都需要在 pjax:complete 中显式重新加载或调用其刷新方法。

四、总结

通过以上步骤,我们不仅给博客添加了一个高颜值的黑胶播放器,更通过 PJAX 技术极大地提升了用户体验。现在,用户可以在浏览文章、查看归档的同时,享受不间断的背景音乐陪伴。

这种“单页应用”般的体验,是现代静态博客优化的重要方向之一。

文章作者:阿文
文章链接: https://www.awen.me/post/a465943d.html
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 阿文的博客