一、部署原理
在 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 官方资源库 、Booth 、Nizima 。或者在 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" : [ ] , "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.x和translate.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.js、waifu-tips.json和waifu.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/目录的渲染,添加以下配置:
这样 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 即可。