花了大半个上午的时间,在 DeepSeek 的帮助下为 Astro Paper 主题引入了对 mdx 和 APlayer 嵌入式音乐播放器的支持。
效果
使用方法
在 mdx 文章中插入如下代码:
import Aplayer from '@/components/APlayer.astro';
<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 手动配置。
npm install @astrojs/mdx
然后在 src/astro.config.ts
中引入 mdx
并在 integrations
部分加入 mdx()
:
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
扩展名的支持即可:
const blog = defineCollection({
loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/data/blog" }),
// 省略其他代码
引入 Aplayer.js 支持
首先使用 npm 安装 aplayer:
npm install aplayer --save
然后,在 src
目录下新增 types
目录,在 src/types/aplayer.d.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.astro
的 header
内加入如下代码,实现全局导入 APlayer。
<script>
// @ts-ignore - 显式声明全局类型
import APlayer from 'aplayer';
// 通过类型断言解决类型问题
(window as unknown as { APlayer: typeof APlayer }).APlayer = APlayer;
</script>
最后,在 src/components
下新增 APlayer.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
中加入如下代码:
/* 移动端优化 */
@media (max-width: 768px) {
.aplayer-container {
width: 90vw;
left: 0rem;
padding: 0rem;
}
}
至此,大功告成。Just enjoy ~