深夜提醒

现在是深夜,建议您注意休息,不要熬夜哦~

🏮 🏮 🏮

新年快乐

祝君万事如意心想事成!

share-image
中
ESC

Hexo Blog Development Notes: Building an Uninterrupted Vinyl Music Player

English 阅读中文版

When browsing a personal blog, background music adds to the immersion. But traditional multi-page blogs refresh the whole page on every navigation, cutting the music off. This article explains in detail how to build a vinyl-style music player in a Hexo blog and use PJAX to enable site-wide navigation without full refreshes, so the music keeps playing.

I. Interface Design: Retro Vinyl Style

Our goal is a mini player with a spinning animation and a tonearm effect, placed below the sidebar navigation.

1. HTML Structure

Add the following structure to the sidebar template (such as layout/partials/sidebar.ejs):

<% if (theme.music && theme.music.enable) { %>
<div class="music-player-wrapper">
<!-- The vinyl record body -->
<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>
<!-- The tonearm -->
<div class="tonearm" id="tonearm"></div>
</div>

<!-- Control buttons -->
<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 element -->
<audio id="bg-music" preload="none"></audio>
</div>
<% } %>

2. CSS Styles and Animation

Use CSS3 to achieve a skeuomorphic effect with the record spinning and the tonearm moving. The core code is below (Stylus):

// Record container
.vinyl-record-container
position relative
width 120px
height 120px
transition all 0.3s ease

// Record spin animation
.vinyl-record.spinning
animation spin 4s linear infinite

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

// Tonearm design
.tonearm
position absolute
top -10px
right -5px
width 8px
height 90px
background #ccc
transform-origin 4px 4px
transform rotate(-35deg) // Initial position: raised
transition transform 0.5s cubic-bezier(0.4, 0, 0.2, 1)

// State while playing: the tonearm moves onto the record
.vinyl-record-container.playing .tonearm
transform rotate(-5deg)

II. Implementing the Playback Logic

Use JavaScript to keep the audio element and the UI state in sync.

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;
// Add the animation classes
document.getElementById('vinyl-record').classList.add('spinning');
document.getElementById('vinyl-container').classList.add('playing');
// Toggle the icon
playBtn.classList.replace('fa-play', 'fa-pause');
});
} else {
audio.pause();
isPlaying = false;
// Remove the animation classes
document.getElementById('vinyl-record').classList.remove('spinning');
document.getElementById('vinyl-container').classList.remove('playing');
playBtn.classList.replace('fa-pause', 'fa-play');
}
}
});

III. The Core Difficulty: Uninterrupted Playback with PJAX

To keep the music playing across page navigations, we need PJAX (PushState + AJAX). Its principle: intercept link clicks, request the new page’s content via AJAX, and replace only the main body of the page (Main Content), keeping the sidebar (where the player lives) and the footer and other rarely-changing areas intact.

1. Adding the Dependencies

Include jQuery and jquery-pjax in layout.ejs:

<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. Initializing PJAX

Configure PJAX to take over all in-site links and specify .main-content as the update container:

$(document).ready(function() {
// Listen for all links that start with the root path
$(document).pjax('a[href^="<%= config.root %>"]', '.main-content', {
fragment: '.main-content', // Which part of the new page to extract
timeout: 8000
});

// Callback after PJAX completes
$(document).on('pjax:complete', function() {
// 1. Reload syntax highlighting
if (window.Prism) window.Prism.highlightAll();

// 2. Reload the visit counter
$.getScript("https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js");

// 3. Other scripts that need re-initializing...
});
});

3. Pitfalls and Solutions

Problem 1: The comment section doesn’t load
Comment plugins like Gitalk usually initialize on page load. After PJAX replaces the content, the old scripts no longer run.
Fix: put the comment script inside the .main-content container, or manually re-instantiate the comment plugin in the pjax:complete callback.

Problem 2: Third-party JS stops working
Things like the Busuanzi counter and math-formula rendering all need to be explicitly reloaded or refreshed in the pjax:complete callback.

IV. Summary

With these steps we not only add a good-looking vinyl player to the blog but also greatly improve the user experience through PJAX. Now users can browse articles and check the archives while enjoying uninterrupted background music.

This “single-page-app-like” experience is one of the important directions for optimizing modern static blogs.

文章作者:阿文
文章链接: https://www.awen.me/post/9c1e58f3.html
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 阿文的博客!
本文于 2026-01-30 14:00 发布,已超过半年(259天),请注意甄别内容是否已过期。

评论

0 条评论
😀😃😄 😁😅😂 🤣😊😇 🙂🙃😉 😌😍🥰 😘😗😙 😚😋😛 😝😜🤪 🤨🧐🤓 😎🥸🤩 🥳😏😒 😞😔😟 😕🙁☹️ 😣😖😫 😩🥺😢 😭😤😠 😡🤬🤯 😳🥵🥶 😱😨😰 😥😓🤗 🤔🤭🤫 🤥😶😐 😑😬🙄 😯😦😧 😮😲🥱 😴🤤😪 😵🤐🥴 🤢🤮🤧 😷🤒🤕 🤑🤠😈 👿👹👺 🤡💩👻 💀☠️👽 👾🤖🎃 😺😸😹 😻😼😽 🙀😿😾 👍👎👏 🙌👐🤲 🤝🤜🤛 ✌️🤞🤟 🤘👌🤏 👈👉👆 👇☝️✋ 🤚🖐️🖖 👋🤙💪 🦾🖕✍️ 🙏💅🤳 💯💢💥 💫💦💨 🕳️💣💬 👁️‍🗨️🗨️🗯️ 💭💤❤️ 🧡💛💚 💙💜🖤 🤍🤎💔 ❣️💕💞 💓💗💖 💘💝💟 ☮️✝️☪️ 🕉️☸️✡️ 🔯🕎☯️ ☦️🛐⛎ ♈♉♊ ♋♌♍ ♎♏♐ ♑♒♓ 🆔⚛️🉑 ☢️☣️📴 📳🈶🈚 🈸🈺🈷️ ✴️🆚💮 🉐㊙️㊗️ 🈴🈵🈹 🈲🅰️🅱️ 🆎🆑🅾️ 🆘❌⭕ 🛑⛔📛 🚫💯💢 ♨️🚷🚯 🚳🚱🔞 📵🚭❗ ❕❓❔ ‼️⁉️🔅 🔆〽️⚠️ 🚸🔱⚜️ 🔰♻️✅ 🈯💹❇️ ✳️❎🌐 💠Ⓜ️🌀 💤🏧🚾 ♿🅿️🈳 🈂🛂🛃 🛄🛅🛗 🚀🛸🚁 🚉🚆🚅 ✈️🛫🛬 🛩️💺🛰️
加载中...

留言反馈

😀😃😄 😁😅😂 🤣😊😇 🙂🙃😉 😌😍🥰 😘😗😙 😚😋😛 😝😜🤪 🤨🧐🤓 😎🥸🤩 🥳😏😒 😞😔😟 😕🙁☹️ 😣😖😫 😩🥺😢 😭😤😠 😡🤬🤯 😳🥵🥶 😱😨😰 😥😓🤗 🤔🤭🤫 🤥😶😐 😑😬🙄 😯😦😧 😮😲🥱 😴🤤😪 😵🤐🥴 🤢🤮🤧 😷🤒🤕 🤑🤠😈 👿👹👺 🤡💩👻 💀☠️👽 👾🤖🎃 😺😸😹 😻😼😽 🙀😿😾 👍👎👏 🙌👐🤲 🤝🤜🤛 ✌️🤞🤟 🤘👌🤏 👈👉👆 👇☝️✋ 🤚🖐️🖖 👋🤙💪 🦾🖕✍️ 🙏💅🤳 💯💢💥 💫💦💨 🕳️💣💬 👁️‍🗨️🗨️🗯️ 💭💤❤️ 🧡💛💚 💙💜🖤 🤍🤎💔 ❣️💕💞 💓💗💖 💘💝💟 ☮️✝️☪️ 🕉️☸️✡️ 🔯🕎☯️ ☦️🛐⛎ ♈♉♊ ♋♌♍ ♎♏♐ ♑♒♓ 🆔⚛️🉑 ☢️☣️📴 📳🈶🈚 🈸🈺🈷️ ✴️🆚💮 🉐㊙️㊗️ 🈴🈵🈹 🈲🅰️🅱️ 🆎🆑🅾️ 🆘❌⭕ 🛑⛔📛 🚫💯💢 ♨️🚷🚯 🚳🚱🔞 📵🚭❗ ❕❓❔ ‼️⁉️🔅 🔆〽️⚠️ 🚸🔱⚜️ 🔰♻️✅ 🈯💹❇️ ✳️❎🌐 💠Ⓜ️🌀 💤🏧🚾 ♿🅿️🈳 🈂🛂🛃 🛄🛅🛗 🚀🛸🚁 🚉🚆🚅 ✈️🛫🛬 🛩️💺🛰️