如何在自己的博客页面使用自定义Live2D模型
在Hugo站点(我使用的是Reimu主题,其他主题也是同理的)中集成自定义Live2D模型,需要配置资源加载、处理CDN问题和大文件等。下面是详细步骤,基于我的实际经验(包括模型加载失败、CDN加载失败、GitHub Raw 400错误和大文件限制等等问题)。
1: 准备Live2D资源
创建GitHub仓库:新建一个公开仓库(如
your_github_username/live2d_api),下载live2d-api仓库的model文件夹以及model.json文件上传到新建仓库。后续对应的文件目录结构也要参考这个库下面的教程。
下载live2d-widget库下dist文件夹的所有内容,上传到仓库。确保分支为main。
如果要上传自己的Live2D模型文件(.moc3、.json、纹理图片等),只需要薅来的模型文件中把model.json或者说model3.json改名为index.json就可以了,但是注意命名不要有中文,会出现乱码。当然上传了新模型的话,对应的modellist也需要对应修改,这部分很简单,可以直接参考live2d-api仓库的教程。关键文件:
waifu-tips.json:交互框内容。(我是下载下来按照自己喜好修改了,如果你觉得原来的也行的话,就用源仓库的也行)。waifu.css和waifu-tips.js:样式和模型加载脚本。modellist.json:模型存放信息,用于加载模型。
关于访问:
每次上传了新的模型,需要点击该仓库的tag,然后新建release,只有这样后续才能使用cdn链接加载你的模型。
注意:仓库必须公开,否则Raw访问会返回400 Bad Request。如果你想吃现成的话当然也可以直接照抄我的仓库,也有一些我嫖到的免费模型:live2d_api
2: 配置Reimu主题中的Live2D
Reimu主题内置Live2D支持(在themes/reimu/static/resources/live2d-widgets@0.9.0/autoload.js)。别的主题也应该有,如果没有的话可以把live2d-widget仓库下载下来把autoload.js保存到跟这个主题对应的位置。我的autoload.js是已经修改过的,所以说可能和原版不太一样,如果原版的不太行的话,可以尝试一下我的:
// live2d_path 参数建议使用绝对路径
const live2d_path = "https://fastly.jsdelivr.net/npm/live2d-widgets@1.0.0-rc.7/dist/";
// 封装异步加载资源的方法
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.type = 'module';
tag.src = url;
}
if (tag) {
tag.onload = () => resolve(url);
tag.onerror = () => reject(url);
document.head.appendChild(tag);
}
});
}
// 避免跨域图片资源问题
const OriginalImage = window.Image;
window.Image = function (...args) {
const img = new OriginalImage(...args);
img.crossOrigin = "anonymous";
return img;
};
window.Image.prototype = OriginalImage.prototype;
// 加载 waifu.css 和 waifu-tips.js
(async () => {
// 备用CDN列表,如果主CDN无法访问会自动尝试备用CDN
const cdnList = [
"https://fastly.jsdelivr.net/gh/nizigen/live2d_api@v1.4/",
"https://cdn.jsdelivr.net/gh/nizigen/live2d_api@v1.4/",
"https://raw.githubusercontent.com/nizigen/live2d_api/v1.4/",
"https://gcore.jsdelivr.net/gh/nizigen/live2d_api@v1.4/"
];
let currentCdnIndex = 0;
let loadSuccess = false;
// 尝试加载资源的函数,支持本地优先+CDN回退
async function tryLoadResources (cdnUrl) {
try {
// 优先尝试加载本地CSS
let cssPromise;
try {
cssPromise = loadExternalResource("/waifu.css", "css");
console.log("尝试加载本地CSS: /waifu.css");
} catch (localError) {
console.warn("本地CSS加载失败,使用CDN:", localError);
cssPromise = loadExternalResource(cdnUrl + "waifu.css", "css");
}
await Promise.all([
cssPromise,
loadExternalResource(cdnUrl + "waifu-tips.js", "js")
]);
loadSuccess = true;
return cdnUrl;
} catch (error) {
console.warn(`CDN ${cdnUrl} 加载失败,尝试下一个...`);
return null;
}
}
// 尝试所有CDN直到成功或全部失败
while (!loadSuccess && currentCdnIndex < cdnList.length) {
const result = await tryLoadResources(cdnList[currentCdnIndex]);
if (result) {
console.log(`成功使用CDN: ${result}`);
break;
}
currentCdnIndex++;
}
if (!loadSuccess) {
console.error("所有CDN都无法访问,Live2D功能将被禁用");
return;
}
const workingCdn = cdnList[currentCdnIndex];
// 配置选项的具体用法见 README.md
try {
console.log("初始化Live2D Widget...");
console.log("工作CDN:", workingCdn);
console.log("waifu-tips.json路径:", workingCdn + "waifu-tips.json");
initWidget({
waifuPath: workingCdn + "waifu-tips.json",
cdnPath: workingCdn,
cubism2Path: live2d_path + "live2d.min.js",
cubism5Path: "https://cubism.live2d.com/sdk-web/cubismcore/live2dcubismcore.min.js",
tools: ["hitokoto", "asteroids", "switch-model", "switch-texture", "photo", "info", "quit"],
logLevel: "info", // 改为info级别以获得更多调试信息
drag: false
});
console.log("Live2D Widget初始化完成");
// 添加必要的CSS样式确保Live2D正确显示
const style = document.createElement('style');
style.textContent = `
#waifu.waifu-active {
bottom: 0 !important;
transform: translateY(0) !important;
}
#waifu.waifu-hidden {
bottom: -1000px !important;
}
#live2d {
display: block !important;
visibility: visible !important;
pointer-events: auto !important;
}
#waifu-tool {
display: block !important;
opacity: 1 !important;
pointer-events: auto !important;
}
#waifu-tips {
display: block !important;
pointer-events: auto !important;
}
`;
document.head.appendChild(style);
// 清除可能导致问题的缓存
localStorage.removeItem("modelId");
localStorage.removeItem("modelTexturesId");
} catch (error) {
console.error("Live2D初始化失败:", error);
console.warn("建议检查网络连接或更换CDN源");
}
// 添加全局错误处理,防止Live2D错误导致页面崩溃
window.addEventListener('error', (event) => {
// 处理Live2D相关的错误
if (event.message && event.message.includes('hitTest')) {
console.warn("Live2D hitTest错误,已自动修复:", event.message);
event.preventDefault(); // 阻止错误传播
return false;
}
// 处理资源加载错误
if (event.filename && (event.filename.includes('live2d') || event.filename.includes('textures.cache'))) {
console.warn("Live2D资源加载错误,已自动处理:", event.message);
event.preventDefault();
return false;
}
});
// 添加额外的检查和调试信息
setTimeout(() => {
const waifuElement = document.getElementById('waifu');
const live2dCanvas = document.getElementById('live2d');
console.log("Live2D元素检查:");
console.log("- waifu元素存在:", !!waifuElement);
console.log("- live2d画布存在:", !!live2dCanvas);
if (waifuElement) {
console.log("- waifu类名:", waifuElement.className);
console.log("- waifu样式:", getComputedStyle(waifuElement).bottom);
}
if (live2dCanvas) {
console.log("- 画布尺寸:", live2dCanvas.width, "x", live2dCanvas.height);
console.log("- 画布样式:", getComputedStyle(live2dCanvas).display, getComputedStyle(live2dCanvas).visibility);
}
// 如果画布存在但不可见,强制显示
if (live2dCanvas && getComputedStyle(live2dCanvas).display === 'none') {
console.log("强制显示Live2D画布");
live2dCanvas.style.display = 'block';
live2dCanvas.style.visibility = 'visible';
}
// 如果waifu元素存在但没有active类,添加它
if (waifuElement && !waifuElement.classList.contains('waifu-active')) {
console.log("添加waifu-active类");
waifuElement.classList.add('waifu-active');
}
// 如果Live2D加载失败,尝试降级方案
if (!live2dCanvas || live2dCanvas.width === 0) {
console.warn("Live2D模型加载失败,尝试降级显示");
// 可以在这里添加降级的静态图片显示
}
}, 3000);
})();
console.log(`
く__,.ヘヽ. / ,ー、 〉
\ ', !-─‐-i / /´
/`ー' L//`ヽ、
/ /, /| , , ',
イ / /-‐/ i L_ ハ ヽ! i
レ ヘ 7イ`ト レ'ァ-ト、!ハ| |
!,/7 '0' ´0iソ| |
|.从" _ ,,,, / |./ |
レ'| i>.、,,__ _,.イ / .i |
レ'| | / k_7_/レ'ヽ, ハ. |
| |/i 〈|/ i ,.ヘ | i |
.|/ / i: ヘ! \ |
kヽ>、ハ _,.ヘ、 /、!
!'〈//`T´', \ `'7'ーr'
レ'ヽL__|___i,___,ンレ|ノ
ト-,/ |___./
'ー' !_,.:
`);
启用Live2D:在
config/_default/params.yml中设置(这就是Reimu专属的配置文件了,别的主题我不清楚,所以推荐大家都来用这个主题喵!):live2d_widgets:enable: trueposition: left # 或 right修改autoload.js:原来的版本会有一个live2d-path,可以直接改成
https://fastly.jsdelivr.net/npm/live2d-widgets@1.0.0-rc.7/dist/,我这里直接使用了多个cdn链接防止有的cdn链接无法访问,编辑themes/reimu/static/resources/live2d-widgets@0.9.0/autoload.js,调整CDN列表以避免加载失败:const cdnList = ["https://cdn.jsdelivr.net/gh/nizigen/live2d_api@main/", // 稳定CDN优先"https://fastly.jsdelivr.net/gh/nizigen/live2d_api@main/","https://gcore.jsdelivr.net/gh/nizigen/live2d_api@main/","https://raw.githubusercontent.com/nizigen/live2d_api/main/" // 作为备用 ];- 为什么这样?GitHub Raw不稳定(易400错误、限流),优先用jsDelivr代理,下面第三点对应的workingcdn其实就是通过cdnlist里面第一个能够使用的链接。还有就是加载Cubism3及以上的live2d模型需要core,我的尝试下
https://fastly.jsdelivr.net/npm/live2d-widgets@1.0.0-rc.7/dist/,这个版本是可以找到对应的core的。
- 为什么这样?GitHub Raw不稳定(易400错误、限流),优先用jsDelivr代理,下面第三点对应的workingcdn其实就是通过cdnlist里面第一个能够使用的链接。还有就是加载Cubism3及以上的live2d模型需要core,我的尝试下
初始化:在autoload.js的initWidget函数配置的仓库:
详细的关于initWidget的参数的问题可以去原仓库看readme,这里不详细介绍了。大家在使用的时候把cdn列表里面的任意一个改为自己的用户名以及仓库名替换这里的workingcdn就好了,live2d_path建议就使用我在第二点提到的那个。
initWidget({
waifuPath: workingCdn + "waifu-tips.json",
cdnPath: workingCdn,
cubism2Path: live2d_path + "live2d.min.js",
cubism5Path: "https://cubism.live2d.com/sdk-web/cubismcore/live2dcubismcore.min.js",
tools: ["hitokoto", "asteroids", "switch-model", "switch-texture", "photo", "info", "quit"],
logLevel: "info", // 改为info级别以获得更多调试信息
drag: false
});
3: 处理大文件(超过20MB的图片)
一些CDN(如jsDelivr)限制单文件20-50MB。如果有些纹理图片太大:
压缩:用WPS图片压缩或者在网上找压缩网站压缩到<20MB替换掉原图片。
通过以上步骤,应该可以在博客中用上自定义Live2D模型!如果遇到问题,欢迎在评论区提出来,如果是我遇到过的问题乐意给大家解答。效果预览:我的博客
(参考:Live2D官方文档、Reimu主题GitHub、live2d-widget、live2d-api)
