前因逛模之屋的时候发现，里面居然有live2d模型的分享，遂下载下来想放到网站上体验，但是发现模型版本是Live2D Cubism 4，而原始插件Pio只支持到Live2D Cubism 2，于是便想将Live2D SDK更新一下，支持最新的模型。实施方案修改模型发现逻辑，同时识别 model.json（Cubism 2）和 *.model3.json（Cubism 3/4），并将画布从 canvas改为 div容器用 pixi-live2d-display 替换 loadlive2d()修改模型发现逻辑 foreach($load as $key => $value){ $single = substr($value, 26); // 优先检测 Cubism 3/4 的 model3.json $model3_files = glob($value . "/*.model3.json"); if(!empty($model3_files)){ $models[$single] = ucfirst($single) . " (Cubism 3/4)"; } // 再检测 Cubism 2 的 model.json else if(file_exists($value . "/model.json")){ $models[$single] = ucfirst($single); } };下载SDKcdn感觉不如本地稳定文件用途pixi.min.jsPixiJS 渲染引擎live2d.min.jsCubism 2 运行库（pio/kurumi 模型需要）live2dcubismcore.min.jsCubism 4 运行库（Nahida 模型需要）pixi-live2d-display.min.js统一加载器，同时支持 Cubism 2/3/4下载链接替换旧api重写 pio.js，用 pixi-live2d-display 的 PIXI.live2d.Live2DModel.from() 替换旧的 loadlive2d()，同时保留所有原有交互功能。主要修改部分：// 加载模型（支持 Cubism 2 model.json 和 Cubism 3/4 *.model3.json） const loadModel = async (modelUrl) => { if (!window.PIXI || !PIXI.live2d) { console.error("pixi-live2d-display 未加载"); return; } // 首次加载时创建 PIXI.Application if (!current.app) { const width = current.container.clientWidth || 280; const height = current.container.clientHeight || 250; current.app = new PIXI.Application({ width: width, height: height, backgroundAlpha: 0, view: current.container.tagName === "CANVAS" ? current.container : undefined }); // 如果容器不是 canvas，将 PIXI 创建的 canvas 插入容器 if (current.container.tagName !== "CANVAS") { current.container.innerHTML = ""; current.container.appendChild(current.app.view); current.app.view.style.width = width + "px"; current.app.view.style.height = height + "px"; } } // 移除旧模型 if (current.model) { current.app.stage.removeChild(current.model); current.model.destroy(); current.model = null; } try { const model = await PIXI.live2d.Live2DModel.from(modelUrl, { autoInteract: false }); // 自适应缩放 const scaleX = current.app.view.width / model.width; const scaleY = current.app.view.height / model.height; const scale = Math.min(scaleX, scaleY); model.scale.set(scale); // 居中 model.x = (current.app.view.width - model.width * scale) / 2; model.y = (current.app.view.height - model.height * scale) / 2; current.model = model; current.app.stage.addChild(model); } catch (e) { console.error("Live2D 模型加载失败:", e); } }; Bug修复后台没有配置"交互提示扩展"，PHP 端 json_decode 得到的是 null，传到 JS 后 prop.content 就是 undefined，访问 .referer 就报错了。加上 prop.content = prop.content || {};function getCanvas() 和 function getLoader() 定义在方法内部，第二次访问页面时会报 Cannot redeclare 致命错误。设置 anchor 为中心点，让模型以中心为基准定位，解决偏移问题。Math.min 会让模型按较短的边来缩放，确保整个模型都在画布内，不会有任何部分被裁切。如果有空白区域也只是某一侧留白，不会影响显示。表情添加由于没有表情相关的代码，需要新加，以下是特性对比特性Cubism 2Cubism 3/4眼球物理（眼珠晃动惯性）无有 - 眼球有弹性和延迟表情系统 (Expression)无独立表情文件有 - .exp3.json 独立表情嘴型同步 (LipSync)简单有 - ParamMouthOpenY自动眨眼 (EyeBlink)需手动实现内置 - ParamEyeLOpen/ParamEyeROpen高级物理引擎简单单摆多层级弹簧物理 (120fps)// 触发表情（仅 Cubism 3/4 模型支持） const triggerExpression = (name) => { if (!current.model) return; const model = current.model; // pixi-live2d-display 的 expression() 方法 if (model.internalModel && model.internalModel.motionManager) { // Cubism 4 模型 if (model.internalModel.settings && model.internalModel.settings.expressions) { const expressions = model.internalModel.settings.expressions; if (expressions.length === 0) return; if (name) { model.expression(name); } else { // 随机触发 const randIdx = Math.floor(Math.random() * expressions.length); model.expression(randIdx); } return true; } // Cubism 2 model.json 中的 expressions if (model.settings && Array.isArray(model.settings.expressions) && model.settings.expressions.length > 0) { const randIdx = Math.floor(Math.random() * model.settings.expressions.length); model.expression(randIdx); return true; } } return false; }; // 获取可用的表情名称列表 const getExpressionNames = () => { if (!current.model) return []; const model = current.model; const names = []; if (model.internalModel && model.internalModel.settings && model.internalModel.settings.expressions) { model.internalModel.settings.expressions.forEach((exp) => { const expName = exp.Name || exp.File.replace(/\.exp3?\.json$/, ""); names.push(expName); }); } if (model.settings && Array.isArray(model.settings.expressions)) { model.settings.expressions.forEach((exp) => { names.push(exp.Name || exp.file || exp); }); } return names; };model3.json 的 FileReferences 里没有 Expressions 字段，只有 Moc、Textures、Physics、DisplayInfo。需要把 Expressions 和 Motions 配置补进 model3.json。"DisplayInfo": "Nahida_1080.cdi3.json", "Expressions": [ {"Name": "Angry", "File": "Angry.exp3.json"}, {"Name": "Happy1", "File": "Happy1.exp3.json"}, {"Name": "Sad1", "File": "Sad1.exp3.json"}, {"Name": "Sad2", "File": "Sad2.exp3.json"}, {"Name": "Shy", "File": "Shy.exp3.json"}, {"Name": "shy_normal", "File": "shy_normal.exp3.json"}, {"Name": "Wink", "File": "Wink.exp3.json"}, {"Name": "Halfeyes", "File": "Halfeyes.exp3.json"}, {"Name": "StarEye", "File": "StarEye.exp3.json"}, {"Name": "HandChange", "File": "HandChange.exp3.json"}, {"Name": "kusa", "File": "kusa.exp3.json"}, {"Name": "black", "File": "black.exp3.json"}, {"Name": "mouthchange", "File": "mouthchange.exp3.json"} ]结语修复之后就可以使用啦