之前偶尔就会在别人的博客上看到 Live2D 看板娘效果,就... 有点眼馋了。

于是就想:

exp

# 前言

本来昨天就在想,将 live2D 相关的东西都弄完,然后将本篇文章写完,以及再写个『年中』总结。

结果果然是人算不如情况,预期总是与实际有所差异,昨天整了一天 live2D。
主要是到处找模型,然后挑选看得上眼的,以及写些自定义修改代码花的时间有点长。
另外为了方便本地预览模型,还整了两个 Python 脚本来辅助,一个用于批量重名名,将入口 json 文件重命名为 index.json,另一个用于批量替换预览:点一下控制窗口,然后刷一下页面就切到下一个模型这种。

今天又加了点新功能,修了点昨天没注意到的 Bug,然后修修改改时间又跑了,今天最多把本篇博客写完就差不多了 (或者可能也写不完,得等明天补充了)。
总结呢怎么着也还是得拖到明天去,毕竟除了本篇博客之外,新下载了十几个 G 的 live2D 模型,还等着我弄到看板娘上去看呢。

# 过程

如果按照 Hexo 来讲的话,大概有三种方式:

  1. 使用 Hexo 插件,直接 npm install --save hexo-helper-live2d 安装 Hexo 插件,Github 地址为 hexo-helper-live2d
  2. 使用第三方做好的 Live2D 代码,然后作自定义修改
  3. 拉取官方 SDK,自己撸一个

个人采用了第二种方式,使用 live2d-widget,并在此基础上作自定义修改。

将其从 Github 上拉下来,放在自己博客的 source 目录下,然后在 head.ejs 处引用 autoload.js 即可。

<!-- 自定义看板娘 -->
<% if (theme.live2d.enable){ %>
<script src="/live2d-widget/autoload.js"></script>
<% } %>

当然,这是最基础和简单的做法,单纯这样,确实可以弄出看板娘,但是模型之类的全都是示例库的。

此时想要做到更换自定义的看板娘,有三种做法:

  1. 按照官方文档自己搭建一个服务器
  2. 直接将模型放在 Hexo source 目录加载
  3. 按照说明文档 fork 示例库,或者自己建立一个模型库,然后修改 model_list.json 列表

第一种就不考虑了,没服务器,而且弄起来也挺麻烦。
第二种会增加博客项目本身大小,每次 deploy 可都是一份消耗,所以也不考虑。

因此个人开始选择了第一种。

分别 fork 以下两个库:

  1. live2d-widget
  2. live2d_api

注:如果觉得这样比较麻烦,也可直接拉取 live2d-widget 库,然后将其文件复制到 Hexo source 目录使用。

fork 之后,发现就算修改了 model_list.json,但是使用 jsdeliver 拉到的 model_list.json 模型配置表还是旧的。

可能是因为 jsdeliver 缓存原因,于是改成支持本地加载配置,然后整理模型。

加载模型列表的代码改在了 autoload.js 初始化的地方,可以直接配置绝对路径,也可以配置相对路径:

initWidget({
    waifuPath: live2d_path + "waifu-tips.json",
    // 模型列表配置路径,如果不配置则默认为 cdnPath 路径下同名文件
    modelListPath: live2d_path + "model_list.json",
    //apiPath: "https://live2d.fghrsh.net/api/",
    //live2d 模型仓库地址
    cdnPath: "https://cdn.jsdelivr.net/gh/CWHISME/live2d_api@master/"
});

效果如下:

live2D

# 关于点击反馈

# 事件

由于网上搜集模型,多数都是提取来的。其配置的 json 有时候默认选项可能无法触发反馈。

辨别方法很简单:当你发现配置中有 flick_head、tap_body 相关 motion 配置,但是点击脑袋或者身体都没反应的话,就说明模型默认不支持。

此时如果想要的话,就需要自己手动设置对应点击区域及其动作了。

如下所示:

"hit_areas_custom":{
    "head_x":[-0.3, 0.8],
    "head_y":[-0.04, 0.43],
    "body_x":[-0.4, 0.29],
    "body_y":[0.17, -0.38]
}

该配置代表自定义了两个点击区域 『头』 『身体』,当点击对应区域时,分别会触发 “flick_head”、“tap_body” 的 Motion 事件

其中 hit_areas_custom 字段的 head_x 和 body_x 定义了头部和身体的 HIT_AREA 的左上角的坐标,head_y 和 b ody_y 定义了右下角的坐标。

获取坐标的方式可以通过修改 live2d.min.js 中 DEBUG_LOG:!0 实现。

live2D

注:如果想直接通过明文 ID 取区域,也可以在 i.prototype.getDrawDataIndex 处打断点,找明文 ID。

内置支持的 Motion 字段有:

  • flick_head:点击脑袋触发
  • tap_body:点击身体触发
  • sleepy: 50000 毫秒后自动播放,index.json 没配置的话,会报错

另外还有『Tap face』会触发播放表情,不过网上下载的模型,基本上都是没有表情的。因此个人对 live2D.min.js 做了点修改:当没有表情时,点击脑袋触发 “flick_head” motion 事件。
另外修改了播放的判断代码,不会由于 motion 优先级而导致后续点击被忽略。

另外经过对 live2D.min.js 代码的查询,目前这个插件用到的 live2d sdk, 好像就支持这么三个触发 motion 事件了。互动事件就两个,一个点击身体,一个点击脑袋。

# 音效

可以参考 nepnep 模型,在点击触发事件中,添加 sound 字段即可。

# 自定义修改

如果有对修改版更有兴趣的,也可以 fork 我修改后的库。

  • live2d-widget
  • live2d_api

其中 model_list.json 增加 defaultModelInfo、sizeZoomList 字段 (还有个 backup 字段可以忽略,没有实际作用)

  • defaultModelInfo:用于配置默认的模型路径
  • sizeZoomList:配置模型可以放大的大小列表

具体还修改了些啥,看了下记录,大概修改有:

  • 支持直接配置本地模型表
  • 增加模型表初始化模型配置
  • 优化随机模型方式,避免反复随机到同一模型,在同一角色没有多个模型时,隐藏换装按钮
  • 增加放大 live2d 模型展示区域的功能
  • 增加 Python 小工具方便预览

# Python 小工具

  • ModifyFileName.py: 用于一键重命名,将网上下载的 .model.json 格式后缀的入口文件重命名为 index.json
  • ModifyFileStr.py:用于更换模型,递归执行,将指定目录下所有存在 index.json 文件的入口进行依次替换入预览代码,回车键更换。

注:后续 ModifyFileStr.py 经过修改后,有预览时选择备份则同时修改 model.json 的功能,如果使用 ModifyFileStr 那么 ModifyFileName 就属于可选项了。

使用方法是:

首先反注释 waifu-tips.js 中代码 loadlive2d("live2d", `/@live2D@/`); ,并注释下方正常加载代码,然后修改 ModifyFileStr.py 中的开头路径配置即可。

y
# 需要修改脚本路径
jsfilePath = r"F:/Hexo/HexoBlog/Hexo/source/live2d-widget/waifu-tips.js"
# 模型路径
live2DPath = r"F:/Hexo/live2dModels/Live2d-model/"
# 如果更换模型时,输入了 “1”,则会同时将当前模型复制到此目录
live2DBackupPath = r"F:/Hexo/live2dModels/fav/"
#模型的网络路径 --- 或者如果是直接拖到 Hexo/Source 目录的话,就是相对路径,如 “/live2D/”
rootDir = "/live2D/"
# 标记符号
defaultName = "/@live2D@/"

其中:

  • 配置相对路径:需要将模型放 source 目录
  • 绝对路径:一个绝对的网络路径,如从 jsdeliver 拉取的

然后执行 py 脚本,按回车会照顺序切换模型。

live2D模型库

# 搭建本地文件服务测试

注 1:可以使用 HFS 搭建本地文件服务,这样就不用把模型拖进 Hexo sourse 目录了,不然可想而知,十几个 G 大小的模型,Hexo 铁定处理不过来的。HFS 添加好对应目录后,网页 js 就可以直接访问了,非常方便。
注 2:实际试过 HFS 之后,发现会有报跨域访问调用问题...

因为跨域访问问题,因此文件服务器还是换成 Nginx 了。

在 nginx.conf 配置 server 字段中添加一列新的 location 配置即可:

location /Live2d-model {
    # 模型绝对路径
    alias   F:/Hexo/live2dModels/Live2d-model/;
    allow all;
    autoindex on;
}

当然,Nginx 如果用只是这样使用默认配置依然还是会有跨域访问问题,因此配置表中还需要加一行:

location /Live2d-model {
    # 模型绝对路径
    alias   F:/Hexo/live2dModels/Live2d-model/;
    allow all;
    autoindex on;
    # 允许跨域访问
    add_header 'Access-Control-Allow-Origin' '*';
    # 禁止缓存,避免本地有问题,修改之后无法即时刷新
    add_header Cache-Control no-store;
    add_header Pragma no-cache;
}

配置完成之后,如果之前启动过 Nginx 服务,则需要使用 nginx -s reload 命令重新加载配置。

# 重新加载配置
nginx -s reload
# 正常关闭
nginx -s quit
# 快速关闭服务
nginx -s stop

然后 Python 代码中, rootDir 路径修改为 Nginx 访问路径:

y
# 需要修改脚本路径
jsfilePath = r"F:/Hexo/HexoBlog/Hexo/source/live2d-widget/waifu-tips.js"
# 模型路径
live2DPath = r"F:/Hexo/live2dModels/Live2d-model/"
# 如果更换模型时,输入了 “1”,则会同时将当前模型复制到此目录
live2DBackupPath = r"F:/Hexo/live2dModels/fav/"
# 模型的网络路径 --- 或者如果是直接拖到 Hexo/Source 目录的话,就是相对路径,如 “/live2D/”
rootDir = "http://127.0.0.1/Live2d-model/"
# 标记符号
defaultName = "/@live2D@/"

执行 Python 代码:

live2D

刷新页面,就可以看到可以正常访问,模型也加载出来了。

live2D

问题解决了之后,还是有点成就感的嘛。不过,等我过两天忘了就想不起来了。

# 其它模型库

因为注意到点击网上找的模型的时候,有时候会报错,如下:

live2D

然后就考虑是不是模型的问题,于是想到了昨天导出找模型找到的一个庞大无比的 live2D 模型库,到如今都还在维护。看日期前些天都在更新。

live2D

昨天也是为感觉这库太大,暂且放弃了

然后呢,正好因为这仓库里边也有之前找的模型,于是今天想替换下来看看,结果分支不能单独拉,就全拉了...
整个模型库达到了十几个 G!虽然 readme 写着有相关工具可以单独下载的,不过在我试图直接拉取的时候,github 竟然达到了满速,十几 M 每秒?!
虽然凭借 github 都经常连不上这种经验感觉不对劲,不过既然速度这么快,于是放弃思考,就让它继续拉了。

所有可以想见,待会我还要筛模型,找好好康的,应该还需要不短的时间的了。


都有报错,最后还是在 live2d.min.js 对应方法处加了个容错判断。


另外这个模型库还有很多是 moc3 格式的,目前这个插件想要用的话,就得改使用的 live2d 渲染插件和代码了。比如同时支持两者模型,根据不同模型调用不同的渲染?不过 moc 的已经够用了。其他的模型暂时也不想看了,等以后什么时候想搞再说吧。

# 问题

# js 修改 css 样式

在使用 js 修改元素 style 的时候,必须符合元素原本规范,比如:设置元素的长宽。
之前我是这样用的

s
var panel = document.getElementById("live2d");
panel.style.height = size;
panel.style.width = size;

结果一直不生效,调试又得出结论找到的元素也没问题,就很为难。
后边左思右想,各种赋值方法也试过,都没法改变,网上也搜索不出什么东西。

后边看着 css 代码发呆的时候,偶然注意到 css 代码赋值参数是有『px』后缀,于是抱着试一试的心态,结果成了!

s
async function zoomModelSize(add = false) {
    if (!modelList) await loadModelList();
    var sizeZoomIndex = sessionStorage.getItem("sizeZoomIndex");
    if (add) {
        sizeZoomIndex++;
        showMessage("好的啦 ~~~", 2000, 9);
    }
    if (sizeZoomIndex >= modelList.sizeZoomList.length||!(typeof(sizeZoomIndex) == 'number' && !isNaN(sizeZoomIndex)))
        sizeZoomIndex = 0;
    var outBoundSize = modelList.sizeZoomList[sizeZoomIndex] + 35;
    if (outBoundSize > window.innerHeight || outBoundSize > window.innerWidth) {
        sizeZoomIndex = 0;
        showMessage("你的浏览器界面太小了,再大一点可就装不下啦 ~", 5000, 9);
    }
    var size = modelList.sizeZoomList[sizeZoomIndex]
    var panel = document.querySelector("#waifu #live2d");
    panel.style.height = size + "px";
    panel.style.width = size + "px";
    sessionStorage.setItem("sizeZoomIndex", sizeZoomIndex);
}

这个问题是在我修改这次博客使用到的 live2d 插件代码,准备加一个『循环放大 / 缩小』效果的时候发现的。
毕竟 js 这东西,以及网页这块,也都是个人在整自己博客的时候用到过,有问题就查文档或搜索。
大概因为这个问题是属于『常识』?碰到过的大概知道,js style 会自动筛除不合法格式的代码,但是给个提示也好么,既没有报错,也不给提示,所说说这些弱类型语言有时候就让人喜欢不起来。

# vscode 快捷键

Shift+Alt 可以像 Vs 一样竖行选择。

# 关于 js 获取移动端高宽

直接使用想要获取屏幕长宽使用 screen.width 或者获取浏览器长宽使用 window.innerWidth 貌似都无法获取到正确结果。

其中获取屏幕长宽 screen.width 可以通过在页面开始加上 meta 标志,使其在移动端也可以生效:

l
<meta name="viewport" content="height=device-height, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0">

而 window.innerWidth 在移动端好像是不行的。所以可以改用 document.body.clientWidth 获取浏览器长宽。

# 结语

live2D
live2D

如同前言所说,开始本来只是打算用插件弄好就收工,计划的是端午节的第一天把 live2D 弄好,并且把本文写了,另外再写个『年中』总结。

结果后边为了测试网上找的模型,又写了两个 Python 小工具,大概就是递归查找模型,然后修改 js 读取代码处的加载路径。
最初也是采用将模型放在 source 目录,然后使用相对路径读取。

后面不是下载了一个超大型模型库么,十几个 G,太大了,直接放 Hexo 里边,硬盘哗啦啦响,半天反应不过来。
于是又配置 nginx 文件服务,使得可以本地读取。

以及一些感觉想改的地方也想改改,搞着搞着,就在改改改这块儿一去不复返了。
今天已经是端午节第三天了,明天就又要上班了。端午节三天,花了可不少时间在这上边,本来计划的学习下 .NetCore 的都没能搞!现在想想还有点不可思议。

另外这两天感觉就是:VSCode 真好用!智能提示真爽快,写 Python、js 之类都真是够方便的,就像 Vs Studio 写 C# 似的。


昨天还是没收完... 今天是 6 月 14 日端午节,终于补充完毕了。

参考文章:
Hexo(sakura)添加 live2d 看板动画
给博客添加能动的看板娘 (Live2D)- 将其添加到网页上吧
Live2DViewerEX 文档