给 Astro 添加 Spine 伪春菜

做了一下午类型体操终于把这沟槽的 Spine 小部件给搞定了,一起来看看怎么折腾的。

起因

起初是在上网冲浪的时候发现这位佬的博客上有个 hnn,顿时觉醒了小学时候折腾 WordPress 时候的记忆。当时的二刺螈特别喜欢在博客上丢个 Live2D 小人叫伪春菜。后来随着 Serverless 的发展,同时也是因为自己前端水平的提高(并不是),我渐渐放弃了 WordPress 转向了 SSG 框架,起初是 Hexo,然后到现在的 Astro。当时 WordPress 上的那个伪春菜必然是没法在 Astro 上使用,而这位的解决方案也是基于 Hexo 的,并不能直接用。怎么办,只有自己写。把我推放在自己的博客上,我靠这样太酷了,非常符合我对前端的想象。

注意

本文假定你正在使用和我博客相同的主题 Akiba。如果你想要根据本文向你自己的 Astro 项目添加伪春菜,可能需要自行调整文件结构。

开整

首先就是模型了哈(玩山药色彩玩的)这个好找,直接去 https://mspine.shinycolors.moe/ 摁 F12 各种模型任你抓。关于模型文件目录是什么个结构,可以参考 Spine 官方文档。我是按模型命名放在 ./public/spine/ 目录下,至于你怎么放,这个后期在编写 astro 网站配置的时候需要。

image.png

然后就该考虑怎么在 Astro 上使用了,这个就有点麻烦了。

首先我自裁,没跟着原站长的思路来,直接去找到 Spine 的文档,果然是大厂解决方案哈,各种平台的 Player 应有尽有(窃喜 ing)。直接按着文档的步骤一步一步来写,本以为能正常运行,最后回车一摁。嗯?啥都没有,怎么会是呢?(此处省略三小时调试)。调试无果,意外发现原站长给了部署指南,此时的我宛如抓到了救命稻草,赶紧看看这篇部署指南。草,原来 Spine 和 Live2D 一样,不同的 Player 对应不同版本导出的模型,旧版本的模型无法在新版本的 Player 上运行。我是直接使用 npm 上的 spine-player 包,它的版本是 4.0+,看了一眼闪耀色彩的模型文件(在 data.json 中的 skeleton 字段)版本是 3.6.53,所以无法运行。但是旧版本的 Player 并没有提供 npm 包,总不能手动升级吧,这样太麻烦了。接着往后看,原来旧版本的播放器是直接通过发布的脚本文件引入的。

引入 spine-widget.js

我们接下来需要的 3.6.53 版本的 Spine 播放器引擎文件可以在 https://github.com/EsotericSoftware/spine-runtimes/tree/3.6.53/spine-ts/build 获取。下载三个文件:spine-widget.js spine-widget.d.ts(后面两个根据项目类型,使用 TypeScript 则需要这个 d.ts 声明文件),我将其放在相对项目根目录的 ./src/scripts/spine-widget/ 目录中。

image.png

编写我自己的 Spine 控制器

少走弯路

首先这个模块没有导出任何东西,所以并不能直接当作 ESM 模块使用。所以我们需要修改一下这个文件,让它能够导出我们需要的东西。这个修改很简单,在 js 文件末尾加上一行

export { SpineModel };

同时将声明文件第一行的:

declare class spine {
    ...
}

修改为:

export namespace spine {
    ...
}

这样就可以在我们自己的代码中引入这个文件了。 接下来我们需要一个自己的 Spine 控制器,我将其放在 ./src/scripts/use-spine.ts 中(此处我参考了 nulla 的代码,因为是 TypeScript 项目所以手动打上了类型)

image.png

由于行数过多就不粘贴在这里,可以自行在此处复制

添加 Spine 小部件

有了 Spine 控制器,我们就可以在 Astro 中使用 Spine 了。首先我们需要在 ./src/data/site-config.ts 中添加 Spine 配置:

关于模型配置的相关内容,你可以使用模型对应版本的 Skeleton Viewer 来查看。在这里你可以查看模型的皮肤、动作名称等信息。

interface SpineConfig {
  enable: boolean;
  models: {
    name: string;
    skin: string;
    animations: {
      start: string;
      idle: string[];
    };
  }[];
  styles: {
    widget: {
      x: string;
      y: string;
    };
  };
}
export const spine: SpineConfig = {
  enable: true,
  models: [
    // 模型配置,这里可以配置多个模型,最后会随机选择一个出现在页面上,name 为模型所在文件夹的名称 skin 为皮肤名称 animations 为动画配置
    {
      name: 'model',
      skin: 'default',
      animations: {
        start: 'start', // 模型加载后立即播放的动作名称
        idle: ['idle1', 'idle2', 'idle3'] // 模型待机时播放的动画,会在多个动作中随机选择一个循环播放
      }
    }
  ],
  styles: {
    widget: {
      x: '10px',
      y: '10px'
    }
  }
};

./src/components 文件夹中建立一个 Astro 的组件文件 SpineWidget.astro,内容如下:

---
import { spine } from '../data/site-config'; // 从后端导入 spine 配置,并反映到前端 id 为 spine-widget-container 的 div 元素的 data-spine-config 属性中
---

<div id="spine-widget-container" class="fixed bottom-10 right-10 md:bottom-5 md:right-5 h-[150px] w-[75px] md:h-[300px] md:w-[150px] z-50" data-spine-config={JSON.stringify(spine)}>
    <div id="spine-widget" class="w-full h-full"></div>
</div>

<script>
    import { SpineModel } from '../scripts/use-spine.ts';
    const spineConfig = JSON.parse(document.getElementById('spine-widget-container')!.getAttribute('data-spine-config') as string); // 从前端获取 spine 配置
    const loadModel = () => {
        new SpineModel("#spine-widget" ,spineConfig); // 实例化 SpineModel
    }
    document.addEventListener('astro:after-swap', () => {
        loadModel(); // 在 Astro 页面切换后重新加载模型,防止模型在页面切换时消失
    });
    document.addEventListener('astro:page-load', () => {
        loadModel(); // 在 Astro 页面加载后加载模型
    }, { once: true }); // 网页首次加载完毕后执行一次
</script>

随后将其导入网站的布局模板文件 ./src/layouts/BaseLayout.astro 中:

---
+import SpinePlayer from '../components/spinePlayer.astro';
---

<body class="bg-main text-main">
    <div class="flex flex-col min-h-screen px-4 md:px-8">
        <Nav />
        {showHeader && <Header />}
        <main class="grow w-full max-w-3xl mx-auto" transition:animate={MyAnim}>
            <slot />
        </main>
        <Footer />
    </div>
+    <SpinePlayer />
</body>

大功告成

这样就可以在你的 Astro 项目中使用 Spine 了,如果你有更多的模型,可以在 ./src/data/site-config.ts 中添加更多的模型配置,Spine 小部件会随机选择一个模型进行展示。

image.png