一、部署原理

  在 hexo + butterfly 博客中部署 Live2D 前端组件,本质是通过引入基于 WebGL 的 JavaScript 库,在网页的<canvas>标签中解析并绘制模型文件。实现过程主要分为模型获取、引擎选择与网页部署三个环节。
  Live2D 模型分为早期版本 Cubism 2.0 和现代版本 Cubism 3.0+,两者在数据结构和渲染管线设计上存在显著差异。Cubism 2.0 的模型文件 .moc 结构较为简单,渲染管线也更为直接。Cubism 3.0+ 引入了更复杂的物理模拟、变形算法和动画系统,其模型文件 .moc3 在解析时需要更高的计算资源和更复杂的渲染逻辑。本教程使用 moc3 模型及其相关资源。
  本教程参考项目 Live2d-Widget-v3,该项目是基于 live2d-widget 项目的二次开发,作者在保留 live2d-widget 核心功能的基础上,接入了对 Cubism 3.0+ 标准的 .moc3 模型文件的支持。可以更加方便地在博客中部署看板娘。

二、模型获取

  常用的 Live2D 模型资源平台包括 Live2D Cubism 官方资源库BoothNizima。或者在 GitHub 搜索 live2d models moc3,国内用户也可以在 Bilibili 搜索 Live2D 模型配布 moc3 免费模型选择自己喜欢的模型。在这些平台上可以找到大量免费和付费的 Live2D 模型资源。
  资源下载时请确保选择包含.moc3文件的资源包,并注意相关版权和使用许可。这里我以 Bilibili 的一个免费模型 Allium 为例(作者:Yuri 幽里_official),下载后解压得到以下文件结构:

ariu/
├── ariu.moc3
├── ariu.model3.json
├── ariu.physics3.json
├── motions/
│ ├── jk包.exp3.json
│ ├── 戴帽子.exp3.json
│ ├── 黑化.exp3.json
│ └── ...
├── textures/
│ ├── texture_00.png
│ ├── texture_01.png
│ ├── texture_02.png
│ └── texture_03.png
  • ariu.moc3是模型的核心文件,包含模型的骨骼、网格、物理属性等信息
  • ariu.model3.json定义模型的结构和资源引用
  • ariu.physics3.json包含物理模拟参数
  • motions/目录下存放不同表情的动画文件*exp3.json
  • textures/目录下存放模型使用的纹理图片

  一些模型还包含下面的文件:

  • *.pose3.json定义模型的默认姿势
  • *.idle_motion3.json定义模型的默认动画
  • *.motions.json定义模型的动作集合
  • *.hit_area3.json定义模型的交互区域
  • *.user_data3.json包含用户自定义数据

  这些文件不是必需的,但可以增强模型的表现力和交互性。

三、模型优化

3.1 贴图资源优化

  考虑到 Web 端对网络请求的敏感性,模型中庞大的 .png 贴图会严重拖慢页面的首屏绘制时间,因此使用格式工厂等软件将模型 textures 目录下的所有 .png 文件转换为高压缩比的 .webp 格式,并修改ariu.model3.json中对应的纹理路径引用:

{
"textures": [
"textures/texture_00.webp",
"textures/texture_01.webp",
"textures/texture_02.webp",
"textures/texture_03.webp"
]
}

  这一步不是必须,你也可以选择将整个模型资源(包括贴图、动画等资源)部署在 CDN 或推送到 github 公共仓库,并在后续配置中修改路径为 CDN 地址以加速加载。

3.2 添加动画资源

  默认情况下 live2d-widget 只会加载默认表情动画,因此需要在模型配置ariu.model3.json中添加 motions 文件夹中表情动画文件.exp3.json的引用:

"FileReferences": {
"Moc": "ariu.moc3",
"Textures": [
// 这里是textures目录下的纹理文件列表
],
"Physics": "ariu.physics3.json",
"DisplayInfo": "ariu.cdi3.json",
"Expressions": [ // 这里仅展示部分表情动画文件的引用
{
"Name": "jk",
"File": "motions/jk包.exp3.json"
},
{
"Name": "love",
"File": "motions/爱心眼.exp3.json"
},
{
"Name": "hat",
"File": "motions/戴帽子.exp3.json"
}
]
},

  其中Expressions字段定义模型的表情动画集合,每个表情动画包含一个名称Name和对应的动画文件路径File。这样在后续配置中就可以通过表情名称来触发表情切换。如果你有其他动画资源,可参考 Live2d-Widget-v3 readme 的 2.3.3 节进行配置。

3.3 添加模型画布配置

  Live2d-Widget-v3 支持通过额外配置来调整画布中模型的大小和位置。在ariu/下添加config.json文件,内容如下:

{
"scale": 1.0,
"translate": {
"x": 0.0,
"y": 0.0
}
}

  后续如果觉得模型偏大、偏小或者偏离中心,可以通过调整这几个浮点数来修正矩阵参数。其中scale控制模型的整体缩放比例,translate.xtranslate.y分别控制模型在水平和垂直方向上的平移偏移量。调整这些参数可以让模型在画布中以最佳状态展示。

四、文件结构配置

  Hexo 会将 source 目录下的文件视为静态资源映射到网站根目录。我目前使用的是 “引擎代码走 CDN,模型资源走本地” 的方式。在博客根目录 source/ 下创建 live2d/ 目录。在目录中创建model_list.json文件,内容如下:

{
"models": [
[
"ariu"
]
],
"messages": [
[
"来自 BiliBili 的 ariu 酱 ~"
]
]
}

  其中models字段定义模型列表,每个模型由一个包含模型文件夹名称的数组表示;messages字段定义模型的欢迎语列表,每个欢迎语由一个包含文本字符串的数组表示。如果你有多个模型,可以在models数组中添加更多的模型文件夹名称,并在messages数组中添加对应的欢迎语,可以参考项目 Live2d-Widget-v3 readme 的 2.3.2 节进行配置。

  然后在 Live2d-Widget-v3 中下载waifu-tips.jswaifu-tips.jsonwaifu.css三个文件,放在 source/live2d/ 目录下。将模型资源放在 source/live2d/model 目录下。最终的文件结构如下:

├── live2d/
│ ├── waifu-tips.js
│ ├── waifu-tips.json
│ ├── waifu.css
│ └── model/
│ └── ariu/
│ ├── ariu.moc3
│ ├── ariu.model3.json
│ ├── ariu.physics3.json
│ ├── config.json
│ ├── motions/...
│ └── textures/...

五、网页部署

  首先在博客的_config.yml中跳过对source/live2d/目录的渲染,添加以下配置:

skip_render:
- live2d/**

  这样 Hexo 在构建时就不会对live2d/目录下的文件进行处理,确保模型资源能够正确地被部署到网站根目录下的/live2d/路径。
  然后在 butterfly 主题的_config.butterfly.yml文件定位到 inject.bottom 节点,将核心调度代码按照 YAML 的多行字符串格式进行 DOM 注入,相关参数说明见注释:

inject:
bottom:
- |
<script>
// PJAX 单例锁,保证 Live2D 核心逻辑在生命周期内只初始化一次,避免重复加载和资源泄漏
if (typeof window.live2d_initialized === 'undefined') {
window.live2d_initialized = true;
// Live2D Widget v3 配置对象
const cdnPath = "https://cdn.jsdelivr.net/gh/letere-gzj/live2d-widget-v3@main";
const config = {
path: {
homePath: "/", // 网站根路径
modelPath: "/live2d/", // live2d 资源与配置路径
cssPath: "/live2d/waifu.css", // live2d 样式表路径
tipsJsonPath: "/live2d/waifu-tips.json", // live2d 提示语配置路径
tipsJsPath: "/live2d/waifu-tips.js", // live2d 提示语脚本路径
live2dCorePath: cdnPath + "/Core/live2dcubismcore.js", // Live2D核心库路径,使用 CDN 加载
live2dSdkPath: cdnPath + "/live2d-sdk.js" // Live2D SDK路径,使用 CDN 加载
},
tools: ["hitokoto", "express", "info", "quit"], // 前端工具栏按钮配置,我在这里做了精简
drag: { enable: false, direction: ["x", "y"] }, // 拖拽配置,这里为模型禁止拖拽
switchType: "order" // 模型切换方式,这里为顺序切换
};
// 资源初始化以及移动端适配
if (screen.width >= 768) {
Promise.all([
loadExternalResource(config.path.cssPath, "css"),
loadExternalResource(config.path.live2dCorePath, "js"),
loadExternalResource(config.path.live2dSdkPath, "js"),
loadExternalResource(config.path.tipsJsPath, "js")
]).then(() => {
if (typeof initWidget !== "undefined") {
initWidget({
waifuPath: config.path.tipsJsonPath,
cdnPath: config.path.modelPath,
tools: config.tools,
dragEnable: config.drag.enable,
dragDirection: config.drag.direction,
switchType: config.switchType
});
}
});
}
// 异步加载资源
function loadExternalResource(url, type) {
return new Promise((resolve, reject) => {
let tag;
if (type === "css") {
tag = document.createElement("link");
tag.rel = "stylesheet";
tag.href = url;
} else if (type === "js") {
tag = document.createElement("script");
tag.src = url;
}
if (tag) {
tag.onload = () => resolve(url);
tag.onerror = () => reject(url);
document.head.appendChild(tag);
}
});
}
}
</script>

  上述 inject 相关设置我做了自定义和简化,更详细的参数配置详见 Live2d-Widget-v3 readme 的 2.1 和 2.2 节。然后hexo clean && hexo g重新构建博客,到这里就能在页面中看到部署成功的 Live2D 看板娘了。

六、进阶配置

6.1 模型视图配置

  模型的位置默认是在左下角,如果想将其移动到右下角,需修改 source/live2d/waifu.css,同时针对 Butterfly 主题右侧的悬浮按钮(如回到顶部、夜间模式)留出安全距离。在waifu.css中找到 #waifu#waifu-tool 这两个选择器,进行如下修改:

#waifu {
bottom: -1000px;
left: auto;
right: 50px;
line-height: 0;
margin-bottom: -10px;
position: fixed;
transform: translateY(3px);
transition: transform .3s ease-in-out, bottom 3s ease-in-out;
z-index: 10;
}

#waifu-tool {
color: #aaa;
opacity: 0;
position: absolute;
right: auto;
left: 0px;
top: 55px;
transition: opacity 1s;
}

  这样修改后模型就会出现在右下角,并且工具栏也会相应地调整到模型左侧,避免与 Butterfly 主题的悬浮按钮发生重叠。

6.2 模型画布配置

  由于这个模型是全身模型,默认配置下人物的表情细节不够清晰。修改模型的config.json使得模型呈现半身照效果。

{
"scale": 2.0,
"translate": {
"x": 0.0,
"y": -0.8
}
}

6.3 模型提示语配置

  waifu-tips.json配置一系列鼠标悬停事件的触发条件和对应的提示语响应。原项目中的配置是针对 NexT 主题等设计的,与 Hexo + Butterfly 主题的 HTML 结构和 class 命名规范完全不同。因此需要对这些触发条件进行调整,确保它们能够正确地映射到 Butterfly 主题的核心 DOM 元素上,从而在与页面交互时能够触发相应的提示语。将/source/live2d/waifu-tips.json文件中 mouseover 数组进行替换:

"mouseover": [
{
"selector": "#live2d",
"text": ["干嘛呢你,快把手拿开~~", "鼠…鼠标放错地方了!", "你要干嘛呀?", "喵喵喵?", "怕怕(ノ≧∇≦)ノ", "非礼呀!救命!", "这样的话,只能使用武力了!", "我要生气了哦", "不要动手动脚的!", "真…真的是不知羞耻!", "Hentai!"]
},
{
"selector": "#site-name",
"text": ["点击这里可以回到主页哦!", "这可是主人的门面呢!"]
},
{
"selector": "#search-button",
"text": ["找不到想看的内容?搜索看看吧!", "在找什么东西呢,需要帮忙吗?"]
},
{
"selector": ".menus_item a[href='/']",
"text": ["点它可以回到首页啦!", "想回到上一页可以使用浏览器的后退功能哦。"]
},
{
"selector": ".menus_item a[href='/archives/']",
"text": ["文章目录都整理在这里啦!", "来看看主人的历史记录吧!"]
},
{
"selector": ".menus_item a[href='/tags/']",
"text": ["点击就可以看文章的标签啦!", "来看看都有哪些标签吧~"]
},
{
"selector": ".menus_item a[href='/categories/']",
"text": ["文章都分类好啦~", "来看看都有哪些分类吧~"]
},
{
"selector": ".avatar-img",
"text": ["我家主人好看吗?", "这是我家主人(*´∇`*)", "发现主人出没地点!"]
},
{
"selector": ".card-info-data a",
"text": ["这是文章的统计信息~", "这里记录着主人的心血哦!"]
},
{
"selector": ".card-info-social-icons a",
"text": ["这里有主人的联系方式!", "要去拜访一下主人的其他阵地吗?"]
},
{
"selector": ".article-title",
"text": ["要看看 <span>{text}</span> 这篇文章吗?", "这篇好像很有意思呢!"]
},
{
"selector": ".article-meta-wrap a",
"text": ["点击这里阅读全文哦!"]
},
{
"selector": "#card-toc",
"text": ["文章太长?看看目录吧!", "这里是文章的大纲哦!"]
},
{
"selector": ".post-meta__tags a",
"text": ["要去看看 <span>{text}</span> 标签么?"]
},
{
"selector": ".article-categories a",
"text": ["要去看看 <span>{text}</span> 分类么?"]
},
{
"selector": ".post-reward .reward-button",
"text": ["主人最近在吃土呢,给他一些钱钱吧~", "要打赏我嘛?好期待啊~"]
},
{
"selector": "#post-comment",
"text": ["想要去评论些什么吗?", "觉得博客不错?快来留言和主人交流吧!"]
},
{
"selector": "#darkmode",
"text": ["眼睛累了吗?切换一下深色模式吧!", "要关灯了吗?"]
},
{
"selector": "#go-up",
"text": ["点它就可以回到顶部啦!", "嗖——的一下飞到上面去!"]
},
{
"selector": "#rightside_config",
"text": ["这里可以调整博客的设置哦!"]
},
{
"selector": ".copy-btn",
"text": ["代码可以直接点击复制哟。"]
},
{
"selector": "#footer-wrap a",
"text": ["去 <span>{text}</span> 逛逛吧。"]
},
{
"selector": "#waifu-tool-hitokoto",
"text": ["猜猜我要说些什么?", "我从青蛙王子那里听到了不少人生经验。"]
},
{
"selector": "#waifu-tool-express",
"text": ["要看看我的其他表情吗?", "变脸术!"]
},
{
"selector": "#waifu-tool-info",
"text": ["想要知道更多关于我的事么?", "你想深入了解我什么呢?"]
},
{
"selector": "#waifu-tool-quit",
"text": ["到了要说再见的时候了吗?", "呜呜 QAQ 后会有期……", "不要抛弃我呀……", "哼,你会后悔的!"]
}
],

  其中selector字段需要修改为 Butterfly 主题中对应元素的 CSS 选择器,text字段中的提示语可以根据需要进行自定义。

6.4 CDN 加速配置

  这里我总结了两种方案,第一种是通过 jsDelivr CDN 加速 github 资源,另一种是使用服务器的对象存储服务(如阿里云 OSS、腾讯云 COS 等)加速资源。

6.4.1 jsDelivr CDN 加速配置

  • 优点:免费、配置简单、全球加速、自动更新
  • 缺点:对于国内服务器访问速度受限、对大文件支持有限、DNS 污染风险

  首先在 Live2d-Widget-v3 项目中下载/Core/live2dcubismcore.js/live2d-sdk.js两个文件,添加到source/live2d/目录下。将live2d/下的所有资源推送到公共 GitHub 仓库,如我的 live2d 仓库(这里如何进行推送不再赘述)。然后在_config.butterfly.yml中将路径修改为 jsDelivr CDN 地址。

const cdnPath = "https://cdn.jsdelivr.net/gh/LuoTian001/live2d@main/";
const config = {
path: {
homePath: "/",
modelPath: cdnPath,
cssPath: cdnPath + "waifu.css",
tipsJsonPath: cdnPath + "waifu-tips.json",
tipsJsPath: cdnPath + "waifu-tips.js",
live2dCorePath: cdnPath + "Core/live2dcubismcore.js",
live2dSdkPath: cdnPath + "live2d-sdk.js"
},
# 此处省略其他配置项...
};

  如果使用你自己的 github 仓库,cdnPath 需要替换为https://cdn.jsdelivr.net/gh/{用户名}/{仓库名}@{标签名}/。到这里就完成了基于 jsDelivr CDN 的加速配置。

6.4.2 阿里云 OSS 加速配置

  • 优点:国内访问速度快、支持大文件、稳定可靠
  • 缺点:需要付费、配置相对复杂

  如果上述 CDN 配置不满足需求,则可以利用国内云服务器厂商的对象存储服务。同样需准备相关资源文件(同 6.4.1 节)。以阿里云 OSS 为例,将模型资源上传到该存储空间中,然后获取该资源的公共访问 URL(该配置过程相当麻烦,后续会单开一个帖子进行说明),修改 _config.butterfly.yml 中的路径配置为该 URL。例如:

const cdnPath = "https://{bucket-name}.{region}.aliyuncs.com/live2d/";
const config = {
path: {
homePath: "/",
modelPath: cdnPath,
cssPath: cdnPath + "waifu.css",
tipsJsonPath: cdnPath + "waifu-tips.json",
tipsJsPath: cdnPath + "waifu-tips.js",
live2dCorePath: cdnPath + "Core/live2dcubismcore.js",
live2dSdkPath: cdnPath + "live2d-sdk.js"
},
# 此处省略其他配置项...
};

  其中{bucket-name}替换为你的 OSS 存储空间名称,{region}替换为你的 OSS 所在地域。完成配置后重新构建博客,模型资源就会通过阿里云 OSS 加速加载了。其他云服务商的对象存储服务(如腾讯云 COS、AWS S3 等)配置方法类似,只需替换为对应的公共访问 URL 即可。