关于live2D看板娘添加至站点的一些细小问题
本文最后更新于85 天前,其中的信息可能已经过时,如有错误请发送邮件到184874483@qq.com

各位在从b站上学习如何“零基础绝美个人博客搭建+agent主题美化+嘉然看板娘”之后,不知道有没有注意到这样一个问题:就是当我们网站部署好之后,我们尝试在切换看板娘形象的时候,他会导致人物形象加载不出来。

如图所示,你看图片中的人物,他的模型没有正确加载出来,但是他旁边的那些附加功能按钮却依旧存在着。特别是当你切换人物形象的时候,原本还好好存在的嘉然一切换就会直接自动消失。况且就算大家都在用这个模型看多了,自然而然它就会腻了。所以我们能不能考虑换一个方式呢?或者换一个形象呢。我想在各位兄弟们AI查询资料,然后又或者从github上面去下载一些插件,但是这还是并不能非常好的去解决问题,因为还是存在着,只要你切换形象就会导致人物消失的问题。如果你启用live2D看板娘33,单一的人物形象以及放在现在不算精致的图片,其实也并不是非常好看,然后你使用了live2d这个插件之后,他却只能仅对也是这个看板娘33号才能正确的渲染出图形,在此期间我也尝试过了去更换模型的方法,又或者去更换一些路径,但是呢,这些都无法解决模型渲染的问题。

所以试着大胆一点。我们直接去找那个一开始学习导入的的文件,然后我们去直接查看他当初是怎么给设计的。首先我们根据他当初给我们提供的live2D的页脚代码,</script><!–live2d–><script src=”/wp-content/themes/agron/agron/live2d/TweenLite.js”></script>….就可以找到如下图所示的文件路径

有图片我们可以轻易得知我们所采用的两个模型,就应该是Diana和Ava接下来我们应该分析这些文件,但是他们基本上都有相似的文件名,所以我们应该先去制定一个比较清晰的策略。

  1. 首先,理解文件类型:特别是  .map  文件

像  pixi.min.js  和  pixi.min.js.map  就是一对典型的例子。 .min.js  文件:这是压缩后的代码文件。为了减少文件体积、加快网络加载速度,源代码中的变量名(如  userCount )会被缩短(如  a ),所有注释、空格和换行符都会被移除。这种文件可读性极差,不适合直接分析。 .min.js.map  文件:这就是源映射文件。它是压缩代码与原始源代码之间的“地图”或“翻译字典”。当你在浏览器开发者工具中调试时,浏览器会读取这个  .map  文件,自动将你看到的压缩后代码映射回原始的、未压缩的、可读性强的源代码。这样你就可以像调试原始代码一样设置断点、查看变量。对于分析来说, .map  文件本身通常不是直接阅读的(它是JSON格式,但人类难以直接理解),它的价值在于让浏览器能为你呈现原始代码。所以你应该优先尝试在浏览器中调试,让浏览器利用  .map  文件为你展示原始代码,而不是直接去读  .min.js  文件。

  1. 分析策略:从整体到局部

面对这样一个文件列表,建议按以下步骤进行,

第一步:寻找入口点

在分析任何项目时,找到程序的起点至关重要。

在图中, load.js  和  pio.js  是最可能的入口点。因为它们没有被压缩(没有  .min  后缀),通常意味着它们是项目的自定义代码,负责初始化和其他核心逻辑。优先打开并阅读  load.js  和  pio.js 。看它们引入了哪些模块(如  Cubism4 ,  PIXI ),以及程序的启动流程。

第二步:理清依赖关系

图中的文件不是孤立的,它们之间存在引用关系,

核心库:pixi.min.js :一个强大的 2D WebGL 渲染引擎,常用于游戏和交互式图形。这里是 Live2D 模型显示的画布。live2dcubismcore.min.js  和  cubism4.min.js :这是 Live2D Cubism 引擎的核心库,负责处理模型数据、解析、渲染和动画。 cubism4  是版本号。TweenLite.js :一个轻量级的动画库,用于创建平滑的过渡动画(比如让模型眨眼、摇头)。

项目文件:pio.js  /  pio_sdk4.js :很可能是基于上述库编写的、用于驱动特定 Live2D 模型的主要业务逻辑。 pio.css :定义模型的显示位置、大小等样式。avatar.png :很可能不是普通的图片,而是 Live2D 模型的纹理图集。

第三步:分层阅读代码

从入口文件开始:精读  load.js  和  pio.js 。关注:import  或全局变量声明,这告诉你它依赖了哪些库。初始化函数(如  init() ),了解启动顺序。事件绑定(如点击、鼠标移动),这是交互逻辑的关键。阅库的文档:对于  PIXI 、 Cubism  SDK 和  TweenLite ,不需要立刻深入其压缩后的代码。先去查阅它们的官方文档,了解基本概念和 API 用法。这能极大提升你分析项目代码的效率。

在浏览器中动态调试:这是最有效的一步。打开包含这个项目的网页.按  F12  打开开发者工具。进入  Sources (源代码)面板。在这里,你应该能看到一个名为webpack:// ,  ./src  或类似的目录,这里面就是通过  .map  文件映射出来的、可读的原始源代码,不过在这里难度还是过大,读起来还是依旧不太方便。

load.js

var 引流 = [
  "https://space.bilibili.com/672328094",
  "https://www.bilibili.com/video/BV1FZ4y1F7HH",
  "https://www.bilibili.com/video/BV1FX4y1g7u8",
  "https://www.bilibili.com/video/BV1aK4y1P7Cg",
  "https://www.bilibili.com/video/BV17A411V7Uh",
  "https://www.bilibili.com/video/BV1JV411b7Pc",
  "https://www.bilibili.com/video/BV1AV411v7er",
  "https://www.bilibili.com/video/BV1564y1173Q",

  "https://www.bilibili.com/video/BV1MX4y1N75X",
  "https://www.bilibili.com/video/BV17h411U71w",
  "https://www.bilibili.com/video/BV1ry4y1Y71t",
  "https://www.bilibili.com/video/BV1Sy4y1n7c4",
  "https://www.bilibili.com/video/BV15y4y177uk",
  "https://www.bilibili.com/video/BV1PN411X7QW",
  "https://www.bilibili.com/video/BV1Dp4y1H7iB",
  "https://www.bilibili.com/video/BV1bi4y1P7Eh",
  "https://www.bilibili.com/video/BV1vQ4y1Z7C2",
  "https://www.bilibili.com/video/BV1oU4y1h7Sc",
]
/*这些是引流url如果想要修改什么可以直接在这里面替换。
这里修改并不会对其他产生任何太大的影响。
 */
const initConfig = {
  mode: "fixed",// 固定定位模式
  hidden: true, // 初始隐藏状态
  content: {
    link: 引流[Math.floor(Math.random() * 引流.length)],/* 从引流数组中随机选择一个B站链接 ,用户点击看板娘时可能跳转到该链接  */
    welcome: ["Hi!"],// 欢迎语
    touch: "",
    skin: ["诶,想看看其他团员吗?", "替换后入场文本"], // 皮肤切换提示
    custom: [
      { "selector": ".comment-form", "text": "Content Tooltip" },// CSS选择器 悬停时显示的文本,当用户鼠标悬停在评论表单上时,显示"Content Tooltip",类似工具提示功能
      { "selector": ".home-social a:last-child", "text": "Blog Tooltip" },
      { "selector": ".list .postname", "type": "read" },//针对文章标题列表,可能在看板娘上显示阅读相关的提示
      { "selector": ".post-content a, .page-content a, .post a", "type": "link" }//检测文章内容中的所有链接,当用户悬停在链接上时,看板娘可能会有特殊反应
    ],/*重点:custom 自定义提示系统
    智能交互 - 看板娘会根据用户在不同页面元素上的行为做出反应
    上下文感知 - 知道用户正在评论、阅读文章还是点击链接
    增强用户体验 - 提供更自然的交互反馈
        */
  },
  night: "toggleNightMode()",// 夜间模式切换函数
  model: [
    "/wp-content/themes/argon/argon/live2d/Diana/Diana.model3.json",
    "/wp-content/themes/argon/argon/live2d/Ava/Ava.model3.json",
  ],
    /*定义了2个Live2D模型文件路径
    模型会随机切换或按顺序加载
    */
  tips: true,// 启用提示功能
  onModelLoad: onModelLoad// 模型加载完成回调
}

function 加载圣·嘉然() {//这个函数是整个看板娘系统的启动入口,负责将配置转化为实际的交互式角色。
    pio_reference = new Paul_Pio(initConfig)//Paul_Pio 是Live2D看板娘的核心控制器类,
    // new Paul_Pio(initConfig) 使用之前的配置对象创建实例,pio_reference 是全局变量,保存实例引用供其他函数调用
   //这里没有使用 const/let/var 声明,说明 pio_reference 是预先定义好的全局变量
  pio_alignment = "left"
 //设置对齐方式,pio_alignment 是另一个全局配置变量,设置看板娘在页面中的定位为左侧,可能的值:"left" | "right" | "center" 等
  // Then apply style
  pio_refresh_style()//调用 pio_refresh_style() 函数应用所有样式设置,这个函数会根据 pio_alignment 和其他配置更新CSS样式,确保看板娘正确显示在指定位置
}
 /*window.onload
  ↓
 加载圣·嘉然()
  ↓
 new Paul_Pio(initConfig)  // 创建Live2D实例
  ↓
 onModelLoad()  // 模型加载回调
  ↓
 playAction()  // 播放动作和文本
  ↓
 TweenLite.to()  // 动画效果
 **/
function onModelLoad(model) {
  const container = document.getElementById("pio-container")//获取DOM元素,container - Live2D模型的容器元素
  const canvas = document.getElementById("pio")//canvas - 实际渲染Live2D的Canvas画布元素,这两个元素由 Paul_Pio 库在初始化时创建
    //提取模型核心组件
  const modelNmae = model.internalModel.settings.name//模型名称提取,从模型配置中获取名称("Diana" 或 "Ava"),用于后续的条件分支判断
  const coreModel = model.internalModel.coreModel//核心模型对象,Live2D模型的核心数据对象,包含所有部件的透明度、位置等信息,后续通过 coreModel._partOpacities 控制部件显示/隐藏
  const motionManager = model.internalModel.motionManager// 动作管理器,控制模型动画和动作播放提供动作状态检测(如 motionManager.state.currentGroup),支持事件监听(如 motionManager.once("motionFinish"))
  /*model (参数)
  ↓
  internalModel (Live2D内部模型)
  ├── settings.name      → modelNmae (模型标识)
  ├── coreModel          → coreModel (部件控制器)
  └── motionManager      → motionManager (动作控制器)
   */


  let touchList = [//touchList 定义了用户点击看板娘时可能触发的所有交互动作。
    {
      text: "点击展示文本1",// 显示的文字内容
      motion: "Idle"// 播放的动作名称
    },
    {
      text: "点击展示文本2",
      motion: "Idle"
    }
  ]//模型个性化配置的基础,后续代码会根据不同模型重写这个数组:如果没有这个配置,看板娘就只是一个不会说话的动画模型,失去了所有的个性化和交互魅力
  /*Diana 模型配置,touchList = [{。。。。}]
  用户点击Canvas
    ↓
  从 touchList 随机选择动作
    ↓
  playAction() 执行
    ├── 显示 text (对话气泡)
    ├── 播放 motion (Live2D动作)
    └── 处理 from/to (部件特效)
  */
  function playAction(action) {//这个函数是整个看板娘交互系统的核心执行引擎,非常复杂。
     //文本显示
    action.text && pio_reference.modules.render(action.text)//pio_reference → 全局Live2D控制器实例(在加载圣·嘉然()中创建),modules.render() → 显示对话气泡文本的方法,
    //modules.render() → 显示对话气泡文本的方法
      //动作播放
    action.motion && pio_reference.model.motion(action.motion)//pio_reference.model → Live2D模型控制器,motion("动作名") → 播放指定的Live2D动作
     //作用:执行如"Tap抱阿草-左手"、"Tap哭 -眼角"等预定义动作

    if (action.from && action.to) {// 这里处理复杂的部件透明度动画,这是一个双阶段动画系统,用于控制Live2D模型特定部件的显示/隐藏。
        //from 阶段 - 动作开始时
        //coreModel._partIds,包含所有部件ID的数组,如:["Part1", "Part2", "Part15", "neko", ...],用于通过名称查找部件在透明度数组中的索引
        //coreModel._partOpacities,数字数组,每个元素对应一个部件的透明度(0-1),例如:[1, 0, 0.5, 1, ...] 表示Part1显示、Part2隐藏、Part3半透明
        //TweenLite.to(),GSAP动画库的调用,语法:TweenLite.to(目标对象, 持续时间, {属性: 值}),这里的作用:在0.6秒内将指定部件的透明度渐变到目标值
      Object.keys(action.from).forEach(id => {
        const hidePartIndex = coreModel._partIds.indexOf(id)
        TweenLite.to(coreModel._partOpacities, 0.6, { [hidePartIndex]: action.from[id] });
        // coreModel._partOpacities[hidePartIndex] = action.from[id]
      })
        // to 阶段 - 动作完成后
        /* motionManager.once("motionFinish"),motionManager → 动作管理器(在onModelLoad中获取),
           once("motionFinish") → 监听动作完成事件(只触发一次),
           当前播放的动作结束时执行回调函数 */

      motionManager.once("motionFinish", (data) => {
        Object.keys(action.to).forEach(id => {
          const hidePartIndex = coreModel._partIds.indexOf(id)
          TweenLite.to(coreModel._partOpacities, 0.6, { [hidePartIndex]: action.to[id] });
          // coreModel._partOpacities[hidePartIndex] = action.to[id]
        })
      })
    }      /*播放动作开始 → 执行from动画 → 等待动作完成 → 执行to动画*/
      /*完整动画流程示例
      *以Ava模型的这个动作为例:
      *{text: "可爱的鸽子鸽子~我喜欢你~",
      * motion: "Tap胸口项链",from: { "Part12": 1 },  // 显示小心心部件
      * to: { "Part12": 0 }     // 隐藏小心心部件
      * }
     执行流程:
      *显示文本:"可爱的鸽子鸽子~我喜欢你~"
      *播放"Tap胸口项链"动作
      * 立即:将Part12(小心心)透明度从当前值渐变到1(显示)
      * 完成后:将Part12透明度渐变到0(隐藏)
      * */
      /*之前未见过的变量来源
      *这些变量都是在闭包作用域中捕获的
      *function onModelLoad(model) {
      * //这些变量在playAction函数外部定义
      * const coreModel = model.internalModel.coreModel      // ← 被playAction使用
      *const motionManager = model.internalModel.motionManager // ← 被playAction使用
      * // playAction函数定义在这里,可以访问外部变量
      * function playAction(action) {
      *  // 使用coreModel和motionManager...
     *    }
     *  }
      * */
      /*playAction 是:
      交互执行器 - 将配置转化为实际效果
       动画协调器 - 同步文本、动作、部件变化
       用户体验核心 - 创造流畅的互动体验
      用户体验核心 - 创造流畅的互动体验
     这个函数让看板娘从一个简单的动画模型升级为有表情变化、有特效、有智能响应的虚拟角色
      * */
      //闭包是JavaScript中非常重要且强大的概念。
      // 简单来说,闭包是指一个函数能够记住并访问其词法作用域中的变量,即使该函数在其词法作用域之外执行。
      //什么是闭包作用域?
      //闭包作用域是由函数和其引用的外部变量组合而成的特殊作用域。当一个函数内部定义了另一个函数,并且内部函数引用了外部函数的变量时,就形成了闭包。
      //JavaScript函数在创建时,会保存其所在的作用域链。即使外部函数执行完毕,只要内部函数仍然存在引用,外部函数的作用域就不会被垃圾回收
  }
  //Canvas点击事件,这串代码是看板娘交互系统的入口点
  canvas.onclick = function () {
    if (motionManager.state.currentGroup !== "Idle") return//第一部分:状态判断机制
     /*判断关系解析:
     * motionManager.state.currentGroup
     * motionManager → 动作管理器(从Live2D模型获取)
     * state.currentGroup → 当前正在播放的动作组名称
     * "Idle" → 空闲/待机状态
     * 判断逻辑:
     *   当前动作 ≠ "Idle" → 返回(不执行点击响应)
     *   当前动作 = "Idle" → 继续执行点击响应
     * 实际效果:
     *   允许点击:当看板娘处于静止状态时
     *   阻止点击:当看板娘正在播放其他动作时
     * 防止动作叠加和冲突,确保用户体验流畅
     * */
    const action = pio_reference.modules.rand(touchList)
      /*pio_reference.modules.rand()
      * pio_reference → 全局Live2D控制器
      * modules.rand(array) → Paul_Pio库提供的随机选择函数
      * 从数组中随机返回一个元素
      * touchList
      * 包含所有可能交互动作的数组
      * 结构:[{text: "...", motion: "..."}, {...}]
      * 执行结果:
      *  // 假设 touchList 有10个动作
      *  // rand() 会随机返回其中1个动作对象
      *  const action = {
      *     text: "有人急了,但我不说是谁~",
      *     motion: "Tap= =  左蝴蝶结"
      *   }
      */
    playAction(action)
      /*第三部分:动作执行
      *playAction(随机选择的action对象)
      *  ↓
      * 显示文本 + 播放动作 + 执行特效
       */
  }
     /*用户点击看板娘Canvas
        ↓
       检查当前状态是否为"Idle"
        ├── 是 → 继续执行
        └── 否 → 直接返回(无响应)
        ↓
      从touchList中随机选择一个action
        ↓
      调用playAction(action)执行该动作
        ↓
      用户看到看板娘说话和做动作
      */
     /*返回函数分析
     *这个点击处理函数的返回值:
     *非Idle状态 → 立即返回 undefined
     *Idle状态 → 执行playAction后返回 undefined
     *实际效果返回值:
     *虽然函数本身返回undefined,但它通过副作用(Side Effects)产生实际效果
     *视觉反馈:看板娘播放动作、显示文本
     *状态变更:motionManager状态从Idle变为其他动作
     *用户感知:用户获得交互响应
     * 这个点击事件处理器的智能之处:
     *   防抖机制 - 防止连续点击导致动作冲突
     *   随机化体验 - 每次点击都有新鲜感
     *   状态感知 - 只在合适的时候响应用户
     *   职责分离 - 只负责触发,不负责具体执行
     * 用户体验角度:
     *    用户感受到的是:"我点击看板娘,她就会随机说一句话并做一个可爱的动作,但如果她正在动的时候我点击,她不会理我"
     * 这创造了自然、智能的虚拟角色交互体验,而不是机械的重复动画。
     * */


   //这段代码是Diana模型的专属初始化逻辑
  if (modelNmae === "Diana") {
      /*条件判断的意义
      * 判断逻辑:
      * 检查当前加载的模型是否为"Diana"
      * 只有Diana模型才执行这些专属设置
      * 其他模型(如Ava)会进入不同的分支
      * */
    container.dataset.model = "Diana" //模型标识设置,在容器元素的 data-model 属性中标记当前模型为 "Diana",便于CSS通过属性选择器应用特定样式
    initConfig.content.skin[1] = ["我是吃货担当 嘉然 Diana~"]
      //配置动态更新
      /*修改初始配置中的皮肤切换文本
      * initConfig.content.skin 原始值:["诶,想看看其他团员吗?", "替换后入场文本"]
      *  修改后:["诶,想看看其他团员吗?", "我是吃货担当 嘉然 Diana~"]
      *为什么这样做:
      *  提供模型专属的入场介绍
      *  当用户切换模型时显示个性化欢迎语
      * */

    playAction({ motion: "Tap抱阿草-左手" })
       /*初始动作播放
       *作用:
       * 立即播放Diana的招牌动作"抱阿草"
       * 给用户第一眼的角色性格展示(吃货、可爱)
       **/

       /* 对之前函数的影响
       * 为之前函数赋予了具体值:
       * 1. 影响 playAction 函数
       *  // 这里调用:
       *   playAction({ motion: "Tap抱阿草-左手" })
       *  // 触发的执行链:
       *    playAction({ motion: "Tap抱阿草-左手" })
       *        → pio_reference.model.motion("Tap抱阿草-左手")
       *        → 看板娘做出抱阿草的动作
       *2. 影响后续的 touchList
       *    在后面的代码中会看到:
       *   touchList = [
       *      { text: "嘉心糖屁用没有", motion: "Tap生气 -领结" },
       *     { text: "有人急了,但我不说是谁~", motion: "Tap= =  左蝴蝶结" },
       *    // ... Diana专属的10种交互
       *   ]
       *  影响用户交互体验
       * 设置了Diana的角色定位:"吃货担当"
       * 定义了初始印象:抱阿草的可爱形象
       *  准备了个性对话:后续点击时的各种反应
       *  在完整初始化流程中的位置
       *  onModelLoad(模型加载完成)
       *     ↓
       * if (modelNmae === "Diana") → 进入Diana专属初始化
       *     ├── 设置容器标识
       *     ├── 更新欢迎文本
       *     └── 播放初始动作
       *     ↓
       *   设置Diana专属的touchList
       *     ↓
       *用户点击 → 触发Diana专属交互
       *
       * */

    touchList = [//这串代码正是为Diana模型赋予具体交互动作和对话内容。
      {
        text: "嘉心糖屁用没有",
        motion: "Tap生气 -领结"// ← 这就是传给动作控制器的指令
      },
        /*执行流程:
         *用户点击Canvas
         *   ↓
         *随机选择这个动作对象
         *   ↓
         *playAction({ text: "...", motion: "Tap生气 -领结" })
         *   ↓
         *pio_reference.model.motion("Tap生气 -领结")  // 传给动作控制器
         *   ↓
         * Live2D模型播放"Tap生气 -领结"这个预定义动作
         * 动作结构:Tap + [部位] + [描述]
        */
        {
        text: "有人急了,但我不说是谁~",
        motion: "Tap= =  左蝴蝶结"
      },
      {
        text: "呜呜...呜呜呜....",
        motion: "Tap哭 -眼角"
      },
      {
        text: "想然然了没有呀~",
        motion: "Tap害羞-中间刘海"
      },
      {
        text: "阿草好软呀~",
        motion: "Tap抱阿草-左手"
      },
      {
        text: "不要再戳啦!好痒!",
        motion: "Tap摇头- 身体"
      },
      {
        text: "嗷呜~~~",
        motion: "Tap耳朵-发卡"
      },
      {
        text: "zzZ。。。",
        motion: "Leave"
      },
      {
        text: "哇!好吃的!",
        motion: "Tap右头发"
      },
    ]


      //Ava模型的增强交互系统,Ava模型的 touchList 确实比Diana模型更复杂、更智能,多出的 from 和 to 属性创造了动态特效系统。
  } else if (modelNmae === "Ava") { //Ava模型的增强交互系统
    container.dataset.model = "Ava"
    initConfig.content.skin[1] = ["我是<s>拉胯</s>Gamer担当 向晚 AvA~"]
    playAction({
      motion: "Tap左眼",
      from: {
        "Part15": 1
      },
      to: {
        "Part15": 0
      }



    })

    touchList = [
      {
        text: "水母 水母~ 只是普通的生物",
        motion: "Tap右手"
      },
      {
        text: "可爱的鸽子鸽子~我喜欢你~",
        motion: "Tap胸口项链",
        from: {
          "Part12": 1 // 动作开始时显示部件
        },
        to: {
          "Part12": 0   // 动作完成后隐藏部件
        }
        /*特效执行流程
        *用户点击 → 播放动作开始
        *    ↓
        * 立即执行 from 动画:显示Part12(小心心)
        *    ↓
        *播放"Tap胸口项链"动作
        *    ↓
        *动作完成时执行 to 动画:隐藏Part12
        *   ↓
        *恢复原始状态
        * */
      },
      {
        text: "好...好兄弟之间喜欢很正常啦",
        motion: "Tap中间刘海",
        from: {
          "Part12": 1
        },
        to: {
          "Part12": 0
        }
      },
      {
        text: "啊啊啊!怎么推流辣",
        motion: "Tap右眼",
        from: {
          "Part16": 1
        },
        to: {
          "Part16": 0
        }
      },
      {
        text: "你怎么老摸我,我的身体是不是可有魅力",
        motion: "Tap嘴"
      },
      {
        text: "AAAAAAAAAAvvvvAAA 向晚!",
        motion: "Tap左眼",
        from: {
          "Part15": 1
        },
        to: {
          "Part15": 0
        }
      }
    ]
      /*这些与上面同理,这里不再过多解释。 */



     //这串代码是 Ava模型的"初始化隐藏系统",它的作用非常关键,这段代码极其重要,绝对不能删除
    canvas.width = model.width * 1.2 //将Canvas画布宽度设置为模型原始宽度的1.2倍
    const hideParts = [ //部件隐藏系统 (核心)
      "Part5", // 晕
      "neko", // 喵喵拳
      "game", // 左手游戏手柄
      "Part15", // 墨镜
      "Part21", // 右手小臂
      "Part22", // 左手垂下
      "Part", // 双手抱拳
      "Part16", // 惊讶特效
      "Part12" // 小心心
    ]
     // 隐藏执行过程:
    const hidePartsIndex = hideParts.map(id => coreModel._partIds.indexOf(id))
    hidePartsIndex.forEach(idx => {
      coreModel._partOpacities[idx] = 0 // 设置透明度为0 = 完全隐藏
    })
  }
}

//这最后两行代码是整个看板娘系统的启动入口,非常重要
var pio_reference
 /*全局变量声明
 *声明一个全局变量 pio_reference
 *用于存储Live2D控制器的实例引用
 *让其他函数(如onModelLoad、playAction)能够访问到控制器
 *
 * */
window.onload = 加载圣·嘉然
 /*页面加载事件绑定
 *将 加载圣·嘉然 函数注册为页面完全加载后的回调
 *当HTML、CSS、图片等所有资源加载完成后自动执行
 *
 * */


 /*执行时机分析
 *浏览器解析HTML/CSS/JS
 *     ↓
 *所有资源加载完成
 *     ↓
 *触发 window.onload 事件
 *     ↓
 *执行 加载圣·嘉然() 函数
 *     ↓
 *创建Live2D实例并显示看板娘
 *
 * 为什么需要这样做?
 *避免DOM未加载错误
 *如果直接调用 加载圣·嘉然():
 * //  错误:脚本执行时DOM可能还未解析
 * <script>
 *  加载圣·嘉然() // 可能找不到pio-container元素
 *   </script>
 *正确的方式:、
 * //  正确:等待DOM完全就绪
 * window.onload = 加载圣·嘉然 // 确保所有元素都已存在
 * 与其他代码的关系
 * 依赖关系:
 *   window.onload (页面加载完成)
 *       ↓
 *   加载圣·嘉然()
 *       ↓
 *   new Paul_Pio(initConfig)
 *       ↓
 *   onModelLoad() [模型加载回调]
 *       ↓
 *   playAction() [用户交互]
 *
 * 全局访问链:
 *  // 其他函数通过这个全局变量访问控制器
 *   function onModelLoad(model) {
 *     // 使用 pio_reference.modules.render() 等
 *   }
 *
 *   function playAction(action) {
 *      // 使用 pio_reference.model.motion() 等
 *  }
 *
 * 潜在问题:单事件限制
 *   window.onload = 加载圣·嘉然 // 会覆盖其他onload处理函数
 * 更好的做法:
 *   // 使用addEventListener避免覆盖
 *  window.addEventListener('load', 加载圣·嘉然)
 *
 * */

/*已调用的函数
*1. 直接调用的函数
*加载圣·嘉然()           // 通过 window.onload 调用
*onModelLoad(model)      // 通过 initConfig 回调调用
*playAction(action)      // 在 onModelLoad 和点击事件中调用
*
* 间接调用的函数
*pio_refresh_style()     // 在 加载圣·嘉然() 中调用
*pio_reference.modules.rand() // 在点击事件中调用
*pio_reference.modules.render() // 在 playAction 中调用
*pio_reference.model.motion() // 在 playAction 中调用
*
* 未明确调用的函数/方法
*1. 潜在的未调用函数
* // 在 custom 配置中引用的可能函数
* toggleNightMode()       // initConfig.night 中引用,但未在代码中直接调用
+2. 依赖外部调用的函数
*  // 这些需要 Paul_Pio 库内部调用:
*pio_reference.model.motion()  // 由库内部动画系统调用
*pio_reference.modules.render() // 由库内部消息系统调用
*
* 外部依赖和启动入口
*启动入口明确:
*  // 主要启动入口
* window.onload = 加载圣·嘉然
*
*需要外部文件完成的函数:
*  // 这些方法在 Paul_Pio 库中定义和调用:
* Paul_Pio.prototype.initialize()
* Paul_Pio.prototype.loadModel()
*Paul_Pio.prototype._onModelLoaded() // 会调用我们的 onModelLoad
* 2. TweenLite 动画库
*  // 在 playAction 中使用,但需要外部引入
*  TweenLite.to() // GSAP 动画库的方法
*3. 潜在的 WordPress 函数
*  // 如果这是 WordPress 主题,可能需要:
*pio_refresh_style() // 可能在主题的其他 JS 文件中定义
*
* 寻找完整启动链的方法
* 1. 检查 HTML 引入<!-- 可能在其他 JS 文件中 -->
<script src="path/to/paul_pio.js"></script>
<script src="path/to/tweenlite.js"></script>
<script src="path/to/theme-functions.js"></script>
*2. 搜索全局函数
*在浏览器控制台中检查:
* // 检查这些函数是否存在
typeof Paul_Pio        // 应该返回 "function"
typeof TweenLite       // 应该返回 "object" 或 "function"
typeof pio_refresh_style // 应该返回 "function"
*
* 3. 查看网络请求
* 检查浏览器 Network 面板:

是否加载了 paul_pio.js?

是否加载了 tweenlite.js?

是否加载了模型文件(.model3.json)?
*
* 可能缺失的调用
* // 配置中引用但未在代码中调用
night: "toggleNightMode()"

// 需要其他地方调用这个函数,比如:
// - 主题的夜间模式按钮
// - 时间自动切换
// - 用户偏好设置
*
*完整的调用链条
*[页面加载] → window.onload → 加载圣·嘉然()
    ↓
new Paul_Pio(initConfig) → 库内部初始化
    ↓
Paul_Pio 加载模型 → 完成后调用 onModelLoad()
    ↓
onModelLoad() → 设置点击事件 → 等待用户交互
    ↓
用户点击 → playAction() → 调用各种库方法
*
*当前代码是完整的,但依赖于:

 Paul_Pio 库 - 主要的 Live2D 控制器

 TweenLite 库 - 动画效果

 模型文件 - Live2D 角色数据

 可能的主题函数 - pio_refresh_style, toggleNightMode
*
*启动入口很明确:window.onload = 加载圣·嘉然

如果看板娘不工作,问题很可能出现在:

外部库没有正确引入

模型文件路径错误

依赖的函数未定义

这就是为什么代码看起来"完整"但可能需要其他文件配合才能正常运行!
*
*
* */

到这里我想我就已经把这个最重要的load.js给你们讲清楚了

Paul_Pio 核心库

这是 Live2D看板娘的核心控制库,与之前的代码形成了完整的看板娘系统。

Paul_Pio 核心库 (这个文件)
↓ 提供
Paul_Pio 类、模块方法、事件系统
↑ 被调用
业务逻辑文件 (之前的代码)
↓ 使用
initConfig 配置、onModelLoad 回调

pio.js

/* ----

# Pio Plugin
# By: Dreamer-Paul
# Modify: journey-ad
# Last Update: 2021.5.4

一个支持更换 Live2D 模型的 Typecho 插件。

本代码为奇趣保罗原创,并遵守 GPL 2.0 开源协议。欢迎访问我的博客:https://paugram.com

---- */
/*
* Paul_Pio 主类 - Live2D看板娘核心控制器
* @param {Object} prop - 配置对象,来自load.js之前代码中的 initConfig
*/
var Paul_Pio = function (prop) {
var that = this; // 保存当前实例的引用,用于内部回调


/*
* 当前状态和DOM元素引用
* 这些元素会在load.js的代码中被使用和覆盖
*/
var current = {
idol: 0, // 当前模型索引 - 用于模型切换
menu: document.querySelector(".pio-container .pio-action"),// 右侧按钮菜单
canvas: document.getElementById("pio"), // Live2D画布 - 在load.js代码中会被重新绑定点击事件
body: document.querySelector(".pio-container"), // 看板娘容器
root: document.location.protocol + '//' + document.location.hostname + '/' // 网站根地址
};

/* - 方法模块 - 提供基础服务,在load.js的代码中被调用 */
var modules = {
/*
* 更换模型 - 循环切换模型文件
* @return {number} 新的模型索引
*/
idol: function () {
current.idol < (prop.model.length - 1) ? current.idol++ : current.idol = 0;
return current.idol;
},

/*
* 创建DOM元素 - 用于创建看板娘的各种UI元素
* @param {string} tag - 标签名
* @param {Object} prop - 属性对象
*/
create: function (tag, prop) {
var e = document.createElement(tag);
if (prop.class) e.className = prop.class;
return e;
},

/*
* 随机选择 - 在load.js的代码中被用于随机选择动作
* @param {Array} arr - 数组
* @return {*} 随机数组元素
*/
rand: function (arr) {
return arr[Math.floor(Math.random() * arr.length + 1) - 1];
},

/*
* 创建对话框方法 - 显示看板娘的对话气泡
* 在load.js的代码中通过 pio_reference.modules.render() 调用
* @param {string|Array} text - 要显示的文本或文本数组
*/
render: function (text) {
if (text.constructor === Array) {
dialog.innerHTML = modules.rand(text);// 如果是数组就随机选择
}
else if (text.constructor === String) {
dialog.innerHTML = text;// 直接显示字符串
}
else {
dialog.innerHTML = "输入内容出现问题了 X_X";// 错误处理
}

dialog.classList.add("active");// 显示对话框

// 3秒后自动隐藏
clearTimeout(this.t);
this.t = setTimeout(function () {
dialog.classList.remove("active");
}, 3000);
},

/*
* 移除方法 - 隐藏看板娘
*/
destroy: function () {
that.initHidden();
localStorage.setItem("posterGirl", 0);// 保存隐藏状态
},

/*
* 是否为移动设备 - 用于移动端适配
* @return {boolean} 是否是移动设备
*/
isMobile: function () {
var ua = window.navigator.userAgent.toLowerCase();
ua = ua.indexOf("mobile") || ua.indexOf("android") || ua.indexOf("ios");

return window.innerWidth < 500 || ua !== -1;
}
};

// 将模块方法暴露给实例,供外部调用
this.modules = modules;
this.destroy = modules.destroy;

/*
* 创建UI元素 - 右侧功能按钮
*/
var elements = {
home: modules.create("span", { class: "pio-home" }),// 首页按钮
skin: modules.create("span", { class: "pio-skin" }),// 换肤按钮
info: modules.create("span", { class: "pio-info" }),// 信息按钮
night: modules.create("span", { class: "pio-night" }), // 夜间模式按钮
close: modules.create("span", { class: "pio-close" }), // 关闭按钮

show: modules.create("div", { class: "pio-show" }) // 显示看板娘的触发器
};

// 创建对话框元素并添加到容器
var dialog = modules.create("div", { class: "pio-dialog" });
current.body.appendChild(dialog);
current.body.appendChild(elements.show);

/* - 提示操作模块 - 处理各种交互提示 */
var action = {
/*
* 欢迎提示 - 根据来源和时间显示不同的欢迎语
*/
welcome: function () {
// 检查访问来源
if (document.referrer !== "" && document.referrer.indexOf(current.root) === -1) {
var referrer = document.createElement('a');
referrer.href = document.referrer;
prop.content.referer ? modules.render(prop.content.referer.replace(/%t/, "“" + referrer.hostname + "”")) : modules.render("欢迎来自 “" + referrer.hostname + "” 的朋友!");
}
// 根据时间显示不同的问候语
else if (prop.tips) {
var text, hour = new Date().getHours();

if (hour > 22 || hour <= 5) {
text = '你是夜猫子呀?这么晚还不睡觉,明天起的来嘛';
}
else if (hour > 5 && hour <= 8) {
text = '早上好!';
}
else if (hour > 8 && hour <= 11) {
text = '上午好!工作顺利嘛,不要久坐,多起来走动走动哦!';
}
else if (hour > 11 && hour <= 14) {
text = '中午了,工作了一个上午,现在是午餐时间!';
}
else if (hour > 14 && hour <= 17) {
text = '午后很容易犯困呢,今天的运动目标完成了吗?';
}
else if (hour > 17 && hour <= 19) {
text = '傍晚了!窗外夕阳的景色很美丽呢,最美不过夕阳红~';
}
else if (hour > 19 && hour <= 21) {
text = '晚上好,今天过得怎么样?';
}
else if (hour > 21 && hour <= 23) {
text = '已经这么晚了呀,早点休息吧,晚安~';
}
else {
text = "奇趣保罗说:这个是无法被触发的吧,哈哈";
}

modules.render(text);
}
else {
modules.render(prop.content.welcome || "欢迎来到本站!");
}
},

/*
* 触摸处理 - 设置画布的点击事件
* 注意:这个默认实现会被load.js代码中的 canvas.onclick 覆盖!
*/
touch: function () {
current.canvas.onclick = function () {
modules.render(prop.content.touch || ["你在干什么?", "再摸我就报警了!", "HENTAI!", "不可以这样欺负我啦!"]);
};
},

/*
* 右侧按钮功能 - 创建和管理所有功能按钮
*/
buttons: function () {
// 返回首页
elements.home.onclick = function () {
location.href = current.root;
};
elements.home.onmouseover = function () {
modules.render(prop.content.home || "点击这里回到首页!");
};
current.menu.appendChild(elements.home);

// 更换模型按钮 - 这里会调用 loadlive2d 并触load.js们的 onModelLoad 回调
elements.skin.onclick = function () {
that.model = loadlive2d("pio", prop.model[modules.idol()], model => {
// 调用load.js的回调函数 - 这是load.js代码的入口点
prop.onModelLoad && prop.onModelLoad(model)
prop.content.skin && prop.content.skin[1] ? modules.render(prop.content.skin[1]) : modules.render("新衣服真漂亮~");
});
};
elements.skin.onmouseover = function () {
prop.content.skin && prop.content.skin[0] ? modules.render(prop.content.skin[0]) : modules.render("想看看我的新衣服吗?");
};
if (prop.model.length > 1) current.menu.appendChild(elements.skin);

// 关于我按钮 - 打开链接
elements.info.onclick = function () {
window.open(prop.content.link || "https://paugram.com/coding/add-poster-girl-with-plugin.html");
};
elements.info.onmouseover = function () {
modules.render("想了解更多关于我的信息吗?");
};
current.menu.appendChild(elements.info);

// 夜间模式
if (prop.night) {
elements.night.onclick = function () {
eval(prop.night); // 执行load.js配置的 toggleNightMode() 函数
};
elements.night.onmouseover = function () {
modules.render("夜间点击这里可以保护眼睛呢");
};
current.menu.appendChild(elements.night);
}

// 关闭看板娘按钮
elements.close.onclick = function () {
modules.destroy();
};
elements.close.onmouseover = function () {
modules.render(prop.content.close || "QWQ 下次再见吧~");
};
current.menu.appendChild(elements.close);
},

/*
* 自定义提示 - 处理load.js在 initConfig 中定义的 custom 配置
* 为页面特定元素添加悬停提示
*/
custom: function () {
prop.content.custom.forEach(function (t) {
if (!t.type) t.type = "default";
var e = document.querySelectorAll(t.selector);

if (e.length) {
for (var j = 0; j < e.length; j++) {
// 阅读类型提示
if (t.type === "read") {
e[j].onmouseover = function () {
modules.render("想阅读 %t 吗?".replace(/%t/, "“" + this.innerText + "”"));
}
}
// 链接类型提示
else if (t.type === "link") {
e[j].onmouseover = function () {
modules.render("想了解一下 %t 吗?".replace(/%t/, "“" + this.innerText + "”"));
}
}
// 自定义文本提示
else if (t.text) {
e[j].onmouseover = function () {
modules.render(t.text);
}
}
}
}
});
}
};

/* - 运行模式 - 支持三种不同的显示模式 */
var begin = {
// 静态模式 - 固定位置显示
static: function () {
current.body.classList.add("static");
},

// 固定模式 - 固定位置但可交互
fixed: function () { // 设置触摸事件(会被load.js覆盖)
action.touch(); action.buttons(); // 设置按钮
},

// 可拖拽模式 - 可以拖动位置
draggable: function () { //设置触摸事件(会被load.js覆盖)
action.touch(); action.buttons(); // 设置按钮

var body = current.body;
// 鼠标按下开始拖拽
body.onmousedown = function (downEvent) {
var location = {
x: downEvent.clientX - this.offsetLeft,
y: downEvent.clientY - this.offsetTop
};

// 鼠标移动时更新位置
function move(moveEvent) {
body.classList.add("active");
body.classList.remove("right");
body.style.left = (moveEvent.clientX - location.x) + 'px';
body.style.top = (moveEvent.clientY - location.y) + 'px';
body.style.bottom = "auto";
}

document.addEventListener("mousemove", move);
// 鼠标抬起时停止拖拽
document.addEventListener("mouseup", function () {
body.classList.remove("active");
document.removeEventListener("mousemove", move);
});
};
}
};

/*
* 初始化方法 - 启动看板娘系统
* @param {boolean} onlyText - 是否只初始化文本
*/
this.init = function (onlyText) {
if (!(prop.hidden && modules.isMobile())) {
if (!onlyText) {
action.welcome(); // 显示欢迎语
// 加载Live2D模型 - 这里会触发load.js的 onModelLoad 回调!
that.model = loadlive2d("pio", prop.model[0], model => {
prop.onModelLoad && prop.onModelLoad(model)
});
}

// 根据配置的模式初始化
switch (prop.mode) {
case "static": begin.static(); break;
case "fixed": begin.fixed(); break; // load.js使用的是这个模式
case "draggable": begin.draggable(); break;
}

// 初始化自定义提示
if (prop.content.custom) action.custom();
}
};

/*
* 初始化隐藏状态 - 当用户关闭看板娘时调用
*/
this.initHidden = function () {
current.body.classList.add("hidden");
dialog.classList.remove("active");

// 设置显示看板娘的触发器
elements.show.onclick = function () {
current.body.classList.remove("hidden");
localStorage.setItem("posterGirl", 1);
that.init();
}
}
// 根据本地存储决定是否显示看板娘
localStorage.getItem("posterGirl") == 0 ? this.initHidden() : this.init();
};

// 控制台版权信息
if (window.console && window.console.log) {
console.log("%c Pio %c https://paugram.com ", "color: #fff; margin: 1em 0; padding: 5px 0; background: #673ab7;", "margin: 1em 0; padding: 5px 0; background: #efefef;");
}

Pio库提供了基础设施,而load.js的代码在此基础上构建了丰富的交互体验,好,接下来我们回到页角当中的live2d有关代码的时候,你可以得出这些代码调用了哪些东西以及他们有哪些依赖。接下来我们就可以可以得出。TweenLite.js – 一个动画库,用于创建平滑的动画效果。live2dcubismcore.min.js – Live2D Cubism核心库,用于解析和渲染Live2D模型。pixi.min.js – 一个2D渲染引擎,Live2D通常使用PixiJS进行渲染。cubism4.min.js – Live2D Cubism 4的运行时库。pio.css – 看板娘的样式文件。pio.js – 是Paul_Pio库,即我们之前分析的那个主控制器。pio_sdk4.js – 可能是针对Cubism 4的SDK适配器。load.js – 是用于加载模型和初始化看板娘的脚本。

<!--live2d--> 
<script src="/wp-content/themes/argon/argon/live2d/TweenLite.js"></script> 
<script src="/wp-content/themes/argon/argon/live2d/live2dcubismcore.min.js"></script>
<script src="/wp-content/themes/argon/argon/live2d/pixi.min.js"></script> 
<script src="/wp-content/themes/argon/argon/live2d/cubism4.min.js"></script> 
<link href="/wp-content/themes/argon/argon/live2d/pio.css" rel="stylesheet" type="text/css"/> 
<script src="/wp-content/themes/argon/argon/live2d/pio.js"></script> 
<script src="/wp-content/themes/argon/argon/live2d/pio_sdk4.js"></script> 
<script src="/wp-content/themes/argon/argon/live2d/load.js"></script>

1. 核心库文件(已部分分析)

pio.js # Paul_Pio主控制器(已分析)
load.js # 我们的自定义代码(已部分分析)

2. Live2D SDK 相关需要重点分析

pio_sdk4.js # Live2D Cubism 4 SDK封装
live2dcubismcore.min.js # Live2D核心引擎
cubism4.min.js # Cubism 4运行时

3. 渲染引擎

pixi.min.js # PixiJS渲染引擎 – Live2D的图形渲染基础

4. 动画系统

TweenLite.js # GSAP动画库 – 负责部件透明度动画

5. 样式文件

pio.css # 看板娘样式 – 对话框、按钮等UI样式

文件依赖关系图

pio.css (样式)

pio.js (Paul_Pio控制器) → 依赖 → pio_sdk4.js (Live2D SDK)
↑ ↑
load.js (我们的代码) live2dcubismcore.min.js (核心引擎)
| ↑
└── 使用 → TweenLite.js (动画) cubism4.min.js (Cubism4运行时)
| ↑
└── 依赖 → pixi.min.js (渲染引擎)

当然我们接下来还有一个pio_sdk4.js文件要分析一下,我们并不知道他到底是那种类似于官方代码,还是个人代码,以及我们还要验证一下他的打包的库是否没有问题。

pio_sdk4.js

/* ----

# Pio SDK 2/3/4 support
# By: jupiterbjy
# Modify: journey-ad
# Last Update: 2021.5.4

To use this, you need to include following sources to your HTML file first.
With this script, you don't have to include `l2d.js`. Testing is done without it.
Basic usage is same with Paul-Pio.

Make sure to call `pio_refresh_style()` upon changing styles on anything related to 'pio-container' and it's children.

To change alignment, modify variable `pio_alignment` to either `left` or `right`, then call `pio_refresh_style()`.

<script src="https://cubism.live2d.com/sdk-web/cubismcore/live2dcubismcore.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/dylanNew/live2d/webgl/Live2D/lib/live2d.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/pixi.js@5.3.6/dist/pixi.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/pixi-live2d-display/dist/index.min.js"></script>

If you have trouble setting up this, check following example's sources.
https://jupiterbjy.github.io/PaulPio_PIXI_Demo/

---- */
 /*pio_sdk4.js (这个文件) - Live2D引擎层
    ↑ 被调用
Paul_Pio (pio.js) - 业务逻辑层
    ↑ 被调用
我们的代码 (load.js) - 个性化交互层
 * */

  //loadlive2d() - 模型加载主函数
function loadlive2d(canvas_id, json_object_or_url, on_load) {
    // Replaces original l2d method 'loadlive2d' for Pio.
    // Heavily relies on pixi_live2d_display.
    // 这个函数被 Paul_Pio 调用,用于加载Live2D模型
    // 参数:
    // - canvas_id: "pio" (画布ID)
    // - json_object_or_url: 模型配置文件路径
    // - on_load: 我们的 onModelLoad 回调函数
    console.log("[Pio] Loading new model")
    /* 在 Pio.js 中调用:
       that.model = loadlive2d("pio", prop.model[0], model => {
       prop.onModelLoad && prop.onModelLoad(model) // 调用我们的回调
        });

        // 在load.js的代码中接收:
       function onModelLoad(model) {
       // 这里获得的就是 loadlive2d 返回的 model 对象
        }
    */
    const canvas = document.getElementById(canvas_id)

    // When pio was start minimized on browser refresh or reload,
    // canvas is set to 0, 0 dimension and need to be changed.
    if (canvas.width === 0) {
        canvas.removeAttribute("height")
        pio_refresh_style()
    }

    // Try to remove previous model, if any exists.
    try {
        app.stage.removeChildAt(0)
    } catch (error) {

    }

    let model = PIXI.live2d.Live2DModel.fromSync(json_object_or_url)

    model.once("load", () => {
        app.stage.addChild(model)

        const vertical_factor = canvas.height / model.height
        model.scale.set(vertical_factor)

        // match canvas to model width
        canvas.width = model.width
        canvas.height = model.height
        pio_refresh_style()

        // check alignment, and align model to corner
        if (document.getElementsByClassName("pio-container").item(0).className.includes("left")){
            model.x = 0
        } else {
            model.x = canvas.width - model.width
        }

        // Hit callback definition
        model.on("hit", hitAreas => {
            if (hitAreas.includes("body")) {
                console.log("[Pio] Touch on body (SDK2)")
                model.motion('tap_body')

            } else if (hitAreas.includes("Body")) {
                console.log("[Pio] Touch on body (SDK3/4)")
                model.motion("Tap")

            } else if (hitAreas.includes("head") || hitAreas.includes("Head")){
                console.log("[Pio] Touch on head")
                model.expression()
            }
        })
        
        on_load(model)
    })

    return model
}


function _pio_initialize_container(){
     //_pio_initialize_container() - DOM结构创建
    // Generate structure
    // 创建看板娘的主要DOM结构:
    let pio_container = document.createElement("div") // 主容器
    pio_container.classList.add("pio-container")
    pio_container.id = "pio-container"
    document.body.insertAdjacentElement("beforeend", pio_container)

    // Generate action
    let pio_action = document.createElement("div")  // 按钮栏
    pio_action.classList.add("pio-action")
    pio_container.insertAdjacentElement("beforeend", pio_action)

    // Generate canvas
    let pio_canvas = document.createElement("canvas")  // Live2D画布
    pio_canvas.id = "pio"
    pio_container.insertAdjacentElement("beforeend", pio_canvas)

    console.log("[Pio] Initialized container.")
}


function pio_refresh_style(){
    // Always make sure to call this after container/canvas style changes!
    // You can set alignment here, but still you can change it manually.

    let pio_container = document.getElementsByClassName("pio-container").item(0)

    pio_container.classList.remove("left", "right")
    pio_container.classList.add(pio_alignment)

    // app.resizeTo = document.getElementById("pio")
}

   //_pio_initialize_pixi() - PixiJS初始化
function _pio_initialize_pixi() {
    // Initialize html elements and pixi app.
    // Must run before pio init.
    // 创建DOM结构
    _pio_initialize_container()

    app = new PIXI.Application({   //调用时机:DOMContentLoaded 事件自动触发
        view: document.getElementById("pio"),
        transparent: true,
        autoStart: true,
    })

    pio_refresh_style()
}


// change alignment to left by modifying this value in other script.
// Make sure to call `pio_refresh_style` to apply changes!
let pio_alignment = "right"  // 在 pio_sdk4.js 中定义:


let app // PixiJS应用实例
window.addEventListener("DOMContentLoaded", _pio_initialize_pixi)
 /*
 *  // 在我们的代码中修改和使用:
function 加载圣·嘉然() {
    pio_alignment = "left"  // 覆盖默认值
    pio_refresh_style()     // 调用 pio_sdk4.js 的函数
}
 *
 *  回调函数传递链
 * pio_sdk4.js: loadlive2d() 加载模型完成
    ↓ 调用
Paul_Pio.js: onModelLoad 回调
    ↓ 调用
我们的代码: onModelLoad() 函数
    ↓ 获得
Live2D模型对象,用于自定义交互
 *
 * 模型对象的关键属性:在 loadlive2d() 中创建的模型对象包含:
 *  model.once("load", () => {
    // 这些属性在load.js的代码中被使用:
    model.internalModel.settings.name    // 模型名称 (Diana/Ava)
    model.internalModel.coreModel        // 核心模型对象
    model.internalModel.motionManager    // 动作管理器

    // 动作播放方法 - 在我们的 playAction 中使用
    model.motion('tap_body')
})
 *
 * 点击事件处理的覆盖关系
 * pio_sdk4.js 的默认点击处理:
 * model.on("hit", hitAreas => {
    if (hitAreas.includes("body")) {
        model.motion('tap_body')  // 播放默认身体点击动作
    } else if (hitAreas.includes("head")) {
        model.expression()        // 播放默认表情
    }
})
* load,js的代码覆盖了这个处理:
* // 在 onModelLoad 中:
canvas.onclick = function () {
    // 完全覆盖默认的hit事件,使用我们的复杂交互系统
    const action = pio_reference.modules.rand(touchList)
    playAction(action)
}
 *
 * 完整的启动流程
 * 1. 页面加载 → DOMContentLoaded 事件
   ↓
2. pio_sdk4.js: _pio_initialize_pixi()
   ├── 创建DOM结构 (.pio-container, #pio画布)
   ├── 初始化PixiJS应用
   └── 设置默认样式
   ↓
3. 我们的代码: window.onload = 加载圣·嘉然()
   ↓
4. new Paul_Pio(initConfig)
   ↓
5. Paul_Pio.js: 调用 loadlive2d("pio", 模型路径, 回调)
   ↓
6. pio_sdk4.js: 加载Live2D模型
   ↓
7. 模型加载完成 → 调用我们的 onModelLoad(model)
   ↓
8. 我们的代码: 设置自定义交互逻辑
*
* 动作播放机制
* pio_sdk4.js 提供的动作接口:
* // 在 loadlive2d 函数中定义:
model.motion('tap_body')     // 播放身体点击动作
model.expression()           // 播放表情

// 在我们的代码中使用:
pio_reference.model.motion("Tap抱阿草-左手")  // 通过Paul_Pio代理调用
*
* 技术架构总结
* pio_sdk4.js 的职责:
*  渲染引擎:PixiJS应用管理
 模型加载:Live2D模型文件加载和解析
 基础交互:提供默认的点击区域检测
 DOM管理:创建看板娘的基本HTML结构
 样式控制:位置对齐和画布尺寸管理
 *
 * 与其他文件的分工:
 * pio_sdk4.js:负责"怎么显示"(技术实现)
   Pio.js:负责"显示什么"(业务逻辑)
   load.js的代码:负责"如何交互"(用户体验)
   *
   * 重要的全局对象
   * // pio_sdk4.js 创建的全局对象:
   window.app           // PixiJS应用实例
   window.pio_alignment // 对齐方式配置

// load.js的代码使用的对象:
window.pio_reference // Paul_Pio实例
   *
 * */

所以你到现在我们明确可以得出一个结论:如果我们想要更换live2d模型,我们应该在load.js中第44行至第48行,修改第45行model:[]中的文件路径,

并且在修改完之后。 修改第253行和第260行中的文件名字,

如果你想修改你网站上通过点击了解更多,使得可以跳转到其他网页与你相关的网页。而非仅跳转到B站网页链接,您只需要修改load.js文件中第1行至,第23行里面的内容,你只需要把这里面的链接改成你自己的链接就行了。

今天我们这篇文章就先到这里,下篇文章我们主要探讨的是对于这个live2d的溯源。

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇