为网站嵌入一个优雅的音乐播放器
为什么要在个人网站里加一个播放器?
音乐不是必需,但它能让体验更完整,我也希望网站能更有一些氛围感。
于是我决定在网站上加入一个悬浮音乐播放器,支持播放网易云歌单,不干扰浏览,且能在页面切换时持续播放。经过一番搜索,我发现了开源项目 NeteaseMiniPlayer V2(简称 NMPv2),由 BHCN STUDIO & 北海的佰川 开发。它轻量、美观、功能完整,而且提供了非常灵活的配置方式。

在我的网站中的最终效果:网站左下角有一个圆形的迷你播放器(可展开),支持播放、暂停、上一首/下一首、音量调节、歌词显示、播放列表,甚至还有黑胶唱片的旋转动画。最关键的是 —— 在无刷新导航(AJAX 页面切换)时,音乐不会中断。
这篇文章我会分享如何将它集成到自己的网站中,包括动态加载、样式适配、与现有主题联动,以及解决常见的坑。
一、选定播放器:为什么是 NeteaseMiniPlayer?
市面上有很多第三方音乐插件,但大多数要么太重(需要引入整个 UI 库),要么依赖外部服务器,要么无法自定义样式。NeteaseMiniPlayer 的设计理念打动了我:
- 通过调用网易云音乐的公开 API 获取歌单和歌曲 URL,无需后端。
- 一个
<div>加上 CSS/JS 就可以工作,支持data-*属性配置。 - 支持固定位置和悬浮位置:可以放在页面角落,也可以嵌入内容中。
- 歌词显示、播放列表、循环/随机模式 一应俱全。
项目的 GitHub 地址:https://github.com/numakkiyu/NeteaseMiniPlayer
作者还提供了一个短代码解析器,可以在 Markdown 中通过 {nmpv2:playlist=xxx} 插入播放器。不过我的需求是全站统一悬浮播放器,所以只需要在全局加载一次。
二、集成方案:动态加载 + 全局单例
我的网站使用了 SPA 风格的 AJAX 导航(自己写的 router.js),点击内部链接时通过 fetch 替换 main 内容,而不是整页刷新。这就要求播放器不能被页面替换时销毁,也不能重复创建。
因此,我设计了一个独立模块 global-music-player.js,它的职责是:
- 检查播放器是否已存在。
- 动态加载 NMPv2 的 CSS 和 JS(仅在需要时加载,减少首屏负担)。
- 创建播放器 DOM 并插入
body。 - 监听
ajax:navigation事件,确保无刷新导航后播放器依然存在(如果意外丢失则重建)。
核心代码大致如下(简化版):
// /js/vendor/global-music-player.js
const PLAYER_CONFIG = {
playlistId: '18022003523',
position: 'bottom-right',
lyric: 'true',
theme: 'auto'
};
async function ensureMusicPlayer() {
if (document.querySelector('.netease-mini-player')) return;
await loadResources(); // 动态加载 CSS 和 JS
const playerDiv = createPlayerElement();
document.body.appendChild(playerDiv);
window.NeteaseMiniPlayer.initPlayer(playerDiv);
}
// 监听页面加载和无刷新导航
window.addEventListener('DOMContentLoaded', ensureMusicPlayer);
window.addEventListener('ajax:navigation', ensureMusicPlayer);
这样,无论用户从哪个页面进入,播放器都会自动出现在右下角,并且页面切换时不会被销毁或重复创建。
三、样式适配:让播放器融入网站主题
NeteaseMiniPlayer 的 CSS 使用了自己的一套颜色变量,但我的网站已经定义了一套完整的深色/浅色主题变量(如 --accent-color、--surface-color、--border-color 等)。如果直接使用原始样式,播放器会和网站视觉风格产生割裂。
于是我重写了播放器的样式文件,将它的颜色变量全部映射到我的主题变量:
/* 适配后的 netease-mini-player-v2.css 片段 */
.netease-mini-player {
--player-primary-bg: var(--surface-color);
--player-secondary-bg: var(--surface-color-secondary);
--player-accent-gradient: linear-gradient(135deg, var(--accent-color) 0%, var(--accent-light) 100%);
--player-shadow-sm: var(--shadow-sm);
--player-shadow-md: var(--shadow-md);
--border-color: var(--border-color);
/* ... */
}
同时,为了让播放器的流动背景动画(播放时出现的光晕效果)也跟随主题色,我使用了 color-mix 函数:
.netease-mini-player::before {
background:
radial-gradient(circle at 15% 20%, color-mix(in srgb, var(--accent-color) 25%, transparent) 0%, transparent 60%),
/* ... */
}
这样,当用户通过主题切换按钮切换深色/浅色模式时,播放器会自动跟随变化(因为 data-theme 属性变化后,CSS 变量重新计算)。播放器本身也支持 data-theme="auto",它会检测宿主主题,但我选择完全交给网站的全局主题系统。
NeteaseMiniPlayer原样式:
![]()
适配后的NeteaseMiniPlayer样式:

四、与 AJAX 无刷新导航的深度集成
前面提到播放器在页面切换时需要保持状态。但还有一个细节:如果播放器处于展开状态(非迷你模式),在 AJAX 加载新页面后,可能会因为新页面中的某些 DOM 操作而意外关闭或丢失事件。我的解决方法是:
- 播放器的 DOM 元素固定挂在 body 下,不在任何被替换的容器内。
- 每次
ajax:navigation事件触发后,不重建播放器,而是检查播放器是否存在。如果存在,什么都不做;如果不存在(极端情况),则重新创建。 - 播放器的内部状态(当前播放歌曲、进度、音量)由 NMPv2 自己维护,无刷新导航不会影响
Audio元素的运行。
另外,为了不重复加载脚本,loadResources 函数使用了幂等性设计:
let resourcesLoaded = false;
let loadingPromise = null;
function loadResources() {
if (resourcesLoaded) return Promise.resolve();
if (loadingPromise) return loadingPromise;
loadingPromise = Promise.all([loadCSS(), loadJS()]).then(() => {
resourcesLoaded = true;
loadingPromise = null;
});
return loadingPromise;
}
这样多个并发调用只会加载一次。
五、最终体验与一些自定义优化
- 右下角悬浮,鼠标移入展开为完整控件,移出一段时间后自动半透明停靠,不遮挡内容。
- 歌词滚动:支持原歌词和翻译歌词,当前行高亮并自动滚动。
- 播放列表:可以查看歌单内所有歌曲,点击切换。
- 响应式:在手机访问时,音量滑条会被隐藏,按钮大小调整,依然可用。
我还额外做了两个优化:
1. 主题切换时刷新播放器颜色
因为播放器的流动背景动画使用了 CSS 变量,而主题切换时 document.documentElement 的 data-theme 变化,CSS 变量会自动更新。但某些浏览器可能需要强制重绘,我简单监听了 themeChanged 事件,重新设置一下播放器的 data-theme 属性(虽然实际上变量已经变了,但为了保险)。
2. 避免自动播放策略拦截
浏览器通常禁止未经用户交互的自动播放。我的策略是:初始化播放器时,不自动播放;如果用户点击了播放按钮,正常播放。如果希望首屏有音乐,可以引导用户点击一次任意位置后启用。NMPv2 内部已经有处理 autoplay 属性的逻辑,但需要用户与页面有过交互。我没有启用自动播放,尊重用户的选择。
六、开源与致谢
这个播放器不是我写的,但我很感激能站在巨人的肩膀上。NeteaseMiniPlayer V2 的作者 北海的佰川 不仅写出了优雅的代码,还提供了详细的文档。如果你也想给自己的网站加一个音乐播放器,强烈推荐去他的 GitHub 仓库看看。
我的所有修改(样式适配、动态加载模块)也开源在个人网站的仓库中,你可以从以下位置找到:
- 播放器样式:
/css/netease-mini-player-v2.css - 播放器脚本:
/js/vendor/netease-mini-player-v2.js - 全局加载器:
/js/vendor/global-music-player.js
七、最后
音乐能传递温度。当你读到我那些关于成长、雪夜、考试的随笔时,如果恰好有一首轻柔的钢琴曲在背景里流淌,或许你就能更贴近我当时的心境。
当然,播放器默认是关闭的,用户需要主动点击播放。我不希望音乐成为干扰 —— 它只是一个可选的、小小的陪伴。
现在,你可以试试点击右下角的播放器,选一首歌,然后继续浏览网站……希望你喜欢。
如果你也想集成,可以这样开始:

最后,感谢开源作者 北海的佰川 的贡献。祝你玩得开心 🎵

评论区