在 Astro 博客实现 Live2D 看板娘
1344 字
7 分钟
在 Astro 博客实现 Live2D 看板娘
前言
在博客页面上添加一个 Live2D 看板娘,可以让网站更加生动有趣。本文介绍如何在 Astro 博客中使用 l2d-widget 库集成 Live2D 模型,支持 Cubism 2 和 Cubism 6 格式,实现菜单交互、提示气泡等功能。
为什么选择 l2d-widget
传统的 Live2D 集成方案通常需要手动引入 Cubism SDK、编写渲染逻辑、处理动画状态机,代码量大且维护成本高。l2d-widget 是一个轻量级封装库,具有以下优势:
- 开箱即用:只需一个函数调用即可完成集成
- 双版本支持:同时兼容 Cubism 2(.moc)和 Cubism 4/6(.moc3)格式
- 内置功能:菜单系统、提示气泡、多模型切换、响应式支持
- 体积小:相比自行打包 SDK 方案,依赖更轻量
安装
pnpm add l2d-widget基本用法
最简只需几行代码即可让 Live2D 模型出现在页面上:
import { createWidget } from "l2d-widget";
createWidget({ model: { path: "/models/snow_miku/model.json" }, position: "bottom-left", size: { width: 200, height: 200 },});在 Astro 中集成
本博客主题 Firefly 已经集成。

创建组件
在 src/components/ 下创建 Live2DWidget.astro 组件:
可参考 Firefly 主题详细的 Live2DWidget.astro 组件文件。
---interface Props { config: { enable: boolean; model: { path: string; volume?: number; scale?: number; x?: number; y?: number } | { path: string; volume?: number; scale?: number; x?: number; y?: number }[]; position?: "bottom-left" | "bottom-right"; size?: number | { width: number; height: number }; primaryColor?: string; transitionDuration?: number; transitionType?: "slide" | "fade"; menus?: { items?: { icon?: string; label: string; action: string }[]; extraItems?: { icon?: string; label: string; action: string }[]; align?: "left" | "right"; }; tips?: { welcomeMessage?: string[]; messages?: string[]; duration?: number; interval?: number; }; responsive?: { hideOnMobile?: boolean; mobileBreakpoint?: number; }; };}
const { config } = Astro.props;
// 构建模型配置,通过 data 属性传递到客户端const models = Array.isArray(config.model) ? config.model : [config.model];const modelConfigs = models.map((m) => ({ path: m.path, ...(m.volume !== undefined && { volume: m.volume }), ...(m.scale !== undefined && { scale: m.scale }), ...(m.x !== undefined && { x: m.x }), ...(m.y !== undefined && { y: m.y }),}));---
<div id="l2d-widget-container" style="position: fixed; z-index: 999;" data-models={JSON.stringify(modelConfigs)} data-position={config.position || "bottom-left"} data-size={JSON.stringify(config.size || 300)} data-primary-color={config.primaryColor || ""} data-tips={JSON.stringify(config.tips || {})} data-responsive={JSON.stringify(config.responsive || {})}></div>客户端脚本
由于 Live2D 需要在浏览器中运行 WebGL 渲染,必须将初始化逻辑放在 <script> 标签中:
import { createWidget } from "l2d-widget";
// 菜单动作映射 - JSON 无法序列化函数,使用字符串标识符const menuActions = { home: () => (window.location.href = "/"), scrollToTop: () => window.scrollTo({ top: 0, behavior: "smooth" }), sleep: (widget) => widget.sleep(), github: () => window.open("https://github.com/your-repo", "_blank"),};
function initWidget() { const container = document.getElementById("l2d-widget-container"); if (!container) return;
const models = JSON.parse(container.dataset.models || "[]"); const position = container.dataset.position || "bottom-left"; const size = JSON.parse(container.dataset.size || "300"); const tips = JSON.parse(container.dataset.tips || "{}");
if (models.length === 0) return;
const options = { model: models.length === 1 ? models[0] : models, position, size, };
// 配置提示气泡 if (tips.welcomeMessage || tips.messages) { options.model.tips = { welcomeMessage: tips.welcomeMessage, messages: tips.messages, duration: tips.duration, interval: tips.interval, }; }
createWidget(options);}
if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", initWidget);} else { initWidget();}配置详解
模型配置
model: { path: "/pio/models/live2d/snow_miku/model.json", volume: 0, // 动作声音音量,0-1,默认 0(静音) scale: 1, // 模型缩放比例 x: 0, // X 轴偏移 y: 0, // Y 轴偏移}支持传入数组配置多个模型,菜单中会自动出现切换按钮:
model: [ { path: "/models/cat-black/model.json" }, { path: "/models/cat-white/model.json" },]菜单系统
l2d-widget 支持自定义菜单项。由于配置需要通过 HTML data 属性传递,而 JSON 无法序列化函数,所以采用 action 字符串 + 客户端映射 的模式:
配置端使用字符串标识:
menus: { items: [ { icon: "mdi:home", label: "返回主页", action: "home" }, { icon: "mdi:arrow-up", label: "返回顶部", action: "scrollToTop" }, { icon: "mdi:sleep", label: "休眠", action: "sleep" }, { icon: "mdi:github", label: "GitHub", action: "github" }, ], align: "right",}客户端脚本中将字符串映射为实际函数:
const menuActions = { home: () => (window.location.href = "/"), scrollToTop: () => window.scrollTo({ top: 0, behavior: "smooth" }), sleep: (widget) => widget.sleep(), github: () => window.open("https://github.com", "_blank"),};提示气泡
配置欢迎消息和循环提示:
tips: { enable: true, // 气泡开关 welcomeMessage: ["你好!", "欢迎来到我的世界!"], messages: ["有什么需要帮助的吗?", "今天天气真不错呢!"], duration: 3000, // 每条提示展示时长(ms) interval: 6000, // 提示循环间隔(ms) offset: { x: 0, y: 0 }, // 位置偏移量(px)}主题色
通过 primaryColor 设置菜单、状态条等 UI 元素的背景色,支持 CSS 变量:
primaryColor: "var(--primary)" // 跟随主题色primaryColor: "rgba(96, 165, 250, 0.9)" // 固定颜色入场动画
transitionDuration: 1500, // 动画时长(ms)transitionType: "slide" as const, // 动画类型:"slide" 或 "fade"响应式
在移动端隐藏看板娘:
responsive: { hideOnMobile: true, mobileBreakpoint: 768,}在布局中使用
在主布局文件中引入组件:
---import Live2DWidget from "@/components/features/Live2DWidget.astro";import { live2dWidgetConfig } from "@/config/pioConfig";---
<!-- 页面内容 -->
{live2dWidgetConfig.enable && <Live2DWidget config={live2dWidgetConfig} />}完整配置示例
export const live2dWidgetConfig = { enable: true, model: { path: "/pio/models/live2d/snow_miku/model.json", volume: 0, scale: 1, x: 0, y: 0, }, position: "bottom-left", size: { width: 200, height: 200 }, primaryColor: "var(--primary)", transitionDuration: 1500, transitionType: "slide", menus: { items: [ { icon: "mdi:home", label: "返回主页", action: "home" }, { icon: "mdi:arrow-up", label: "返回顶部", action: "scrollToTop" }, { icon: "mdi:sleep", label: "休眠", action: "sleep" }, { icon: "mdi:github", label: "GitHub", action: "github" }, ], align: "right", }, tips: { enable: true, welcomeMessage: ["你好!", "欢迎来到我的世界!"], messages: ["有什么需要帮助的吗?", "今天天气真不错呢!"], duration: 3000, interval: 6000, offset: { x: 0, y: 0 }, }, responsive: { hideOnMobile: true, mobileBreakpoint: 768, },};注意事项
- 模型资源文件放在
public/目录下,路径以/开头 - Cubism 2 模型文件为
.json+.moc,Cubism 6 为.model3.json+.moc3 - Live2D 渲染依赖 WebGL,确保目标浏览器支持
参考
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!
在 Astro 博客实现 Live2D 看板娘
https://blog.cuteleaf.cn/posts/dev-notes/astro-live2d-mascot/ 相关文章 智能推荐
1
Astro 静态博客文章推荐功能的实现思路
开发笔记 基于标签匹配 + 标题相似度 + 时间衰减 + 分类加成的综合评分算法,在 Astro 静态博客中实现相关文章智能推荐与随机文章推荐的双栏组件。
2
Astro 静态博客文章加密功能的实现思路
开发笔记 在Astro纯静态博客中实现文章密码保护——构建时 AES-256-GCM 加密,客户端 Web Crypto API 解密,零服务端依赖。
3
为我的Astro博客支持响应式图像和AVIF格式
开发笔记 基于 Astro 5.10.0 的响应式图像特性,为 Firefly 博客主题模板添加响应式图像支持和 AVIF 格式优化,提升图片加载性能。
4
在 Astro 博客中为 Markdown 添加多图并排的自适应网格画廊功能
开发笔记 通过编写自定义的 Remark 插件,实现 Markdown 文章中非常实用的多图并排自适应画廊效果。
5
Fuwari/Firefly 升级到 Content Layer API
开发笔记 介绍如何将 Fuwari/Firefly 博客模板从 Astro v4 Content Collections API 升级到 Astro v5 Content Layer API 并修复封面无法显示的问题,包括文件路径处理和组件适配的完整解决方案。
随机文章 随机推荐