Aller au contenu
白鼠的小站
Rechercher
Changer de langue
Retour

为 Astro Paper 引入 mdx 和 Aplayer 支持

2 min de lecture

花了大半个上午的时间,在 DeepSeek 的帮助下为 Astro Paper 主题引入了对 mdxAPlayer 嵌入式音乐播放器的支持。

效果

使用方法

在 mdx 文章中插入如下代码:

mdx
import { APlayer } from "@/components/mdx"; <APlayer fixed={false} autoplay={false} audio={[ { name: "Song Name", artist: "Artist", url: "Song.mp3", cover: "Cover.png", lrc: "lrc.lrc", }, ]} />

为 Astro Paper 主题加入 mdx 支持

参考了 Astro 官方文档中的配置方法

因为我的 pnpm 由于莫名其妙的符号链接问题炸了,所以这里使用 npm 手动配置。

bash
npm install @astrojs/mdx

然后在 src/astro.config.ts 中引入 mdx 并在 integrations 部分加入 mdx()

ts
import mdx from '@astrojs/mdx'; export default defineConfig({ site: SITE.website, integrations: [ sitemap({ filter: page => SITE.showArchives || !page.endsWith("/archives"), }), mdx() ],

最后在 src/content.config.ts 的内容集合 loader 中加入对 .mdx 扩展名的支持即可:

ts
const blog = defineCollection({ loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/data/blog" }), // 省略其他代码

引入 Aplayer.js 支持

部分参考了 boycott 大佬编写的 相关教程

首先使用 npm 安装 aplayer:

bash
npm install aplayer --save

然后,在 src 目录下新增 types 目录,在 src/types/aplayer.d.ts 中编写类型定义:

ts
declare module "aplayer" { interface Audio { name: string; artist: string; url: string; cover: string; lrc: string; [key: string]: any; } interface APlayerOptions { container: HTMLElement; fixed?: boolean; autoplay?: boolean; mini?: boolean; theme?: string; loop?: "all" | "one" | "none"; order?: "list" | "random"; volume?: number; audio: Audio[]; } class APlayer { constructor(options: APlayerOptions); currentTime: number; paused: boolean; volume: number; play(): void; pause(): void; destroy(): void; } export default APlayer; } // 扩展 Window 类型 interface Window { APlayer: typeof import("aplayer").default; }

接着,在 src/layouts/Layout.astroheader 内加入如下代码,实现全局导入 APlayer。

astro
<script> // @ts-ignore - 显式声明全局类型 import APlayer from 'aplayer'; // 通过类型断言解决类型问题 (window as unknown as { APlayer: typeof APlayer }).APlayer = APlayer; </script>

最后,在 src/components 下新增 APlayer.astro 文件,加入如下代码:

astro
--- import 'aplayer/dist/APlayer.min.css'; interface Props { audio: Array<{ name: string; artist: string; url: string; cover: string; lrc: string; [key: string]: any; }>; fixed?: boolean; mini?: boolean; theme?: string; loop?: 'all' | 'one' | 'none'; order?: 'list' | 'random'; volume?: number; } const { audio, fixed = false, mini = false, theme = '#b7daff', loop = 'all', order = 'list', volume = 0.7, } = Astro.props; const instanceId = Math.random().toString(36).substr(2, 9); const config = JSON.stringify({ audio: audio.map(track => ({ ...track, lrc: track.lrc })), fixed, mini, theme, loop, order, volume, autoplay: false }); --- <div id={`aplayer-${instanceId}`} class="aplayer-container" data-config={config}></div> <script is:inline define:vars={{ instanceId, config }}> (function() { let player = null; const initPlayer = () => { const container = document.getElementById(`aplayer-${instanceId}`); if (!container || player) return; const config = JSON.parse(container.dataset.config); player = new APlayer({ container: container, ...config, lrcType: 3 // 使用内置歌词加载功能 }); // 基础事件处理 player.on('error', (error) => { console.error('播放器错误:', error); }); }; const destroyPlayer = () => { if (player) { player.destroy(); player = null; } }; // Astro页面事件监听 document.addEventListener('astro:page-load', initPlayer); document.addEventListener('astro:before-swap', destroyPlayer); // 初始加载 if (document.readyState === 'complete') { initPlayer(); } else { document.addEventListener('DOMContentLoaded', initPlayer); } // 清理播放器 window.addEventListener('beforeunload', destroyPlayer); })(); </script> <style> .aplayer-container { margin: 1rem 0; border-radius: 10px; overflow: hidden; } </style>

现在,应该可以在 mdx 文章中使用前述代码嵌入 APlayer 音乐播放器了。

不过,到这一步为止,我们在文章中嵌入的 APlayer 播放器还不能很好地适配移动端布局。为此,可以在 src/styles/global.css 中加入如下代码:

css
/* 移动端优化 */ @media (max-width: 768px) { .aplayer-container { width: 90vw; left: 0rem; padding: 0rem; } }

至此,大功告成。Just enjoy ~


Commentaires