Skip to content
返回

为 Astro Paper 引入 mdx 和 Aplayer 支持

Published:  at  03:05

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

效果

使用方法

在 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 支持

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

首先使用 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.astroheader 内加入如下代码,实现全局导入 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 ~



下一篇
新的开始,我与博客