live 2d 在網頁上
在網頁上載入 Live2D 模型
TL;DRWeb 端載 Live2D 需要兩個 script:live2dcubismcore.min.js(官方 SDK runtime)+ live2d.js(載入器,通常用 galnetwen/Live2D)。Next.js 用 <Script> 元件管理載入時機,搭配 <canvas id="live2d"> 容器。
主要參考
- galnetwen/Live2D — Web 上展示 Live2D
- hexo-helper-live2d — Hexo 的 Live2D 插件,參考其載入策略
為什麼要折騰 Live2D
想做一個自己的 2D 形象:
- 實況用(OBS 上疊一個會跟頭動的角色)
- 網站上放一個 互動角色(滑鼠經過會看你)
- 剪輯影片用(放轉場、配旁白)
Live2D 是 介於靜態插畫跟全 3D 模型之間 的方案:用 2D 圖層做骨架變形,動起來像紙片動畫,門檻比 VRoid / Blender 低。
必要檔案
| 檔案 | 來源 | 角色 |
|---|---|---|
live2dcubismcore.min.js | Live2D 官方 SDK | runtime,所有功能都依賴它 |
live2d.js | 第三方載入器(galnetwen 等) | 把模型畫到 canvas 的便利 wrapper |
model.json + 貼圖 + 動作檔 | 模型本體 | 你要展示的角色資料 |
官方 Cubism SDK 是付費商業使用、個人免費,引用官方 CDN 即可。
Next.js 整合範例
import Script from 'next/script';
import { useState } from 'react';
import styles from './Live2d.module.scss';
export default function Live2d() {
const [message, setMessage] = useState<string>('');
return (
<>
<div className={styles.live2d_container}>
<canvas
id="live2d"
width="280"
height="250"
className={styles.live2d_canvas}
onMouseMove={(e) => {
e.stopPropagation();
e.preventDefault();
setMessage('你在看我嗎?');
}}
onContextMenu={(e) => {
e.stopPropagation();
e.preventDefault();
}}
onDragStart={(e) => {
e.stopPropagation();
e.preventDefault();
}}
/>
<div className={styles.live2d_message}>{message}</div>
</div>
{/* 1. 先載 Cubism core */}
<Script src="https://cubism.live2d.com/sdk-web/cubismcore/live2dcubismcore.min.js" />
{/* 2. 再載 wrapper */}
<Script src="/live2d/js/live2d.js" />
{/* 3. 等前兩個載完才呼叫 loadlive2d */}
<Script type="text/javascript" strategy="lazyOnload">
{`loadlive2d("live2d", "/live2d/model/tia/model.json");`}
</Script>
</>
);
}
<Script> 載入策略
strategy | 行為 | 用在 |
|---|---|---|
beforeInteractive | SSR 階段就載入 | 必須在 hydration 前到位的關鍵 script |
afterInteractive (預設) | hydration 後載 | 大部分第三方 script |
lazyOnload | browser idle 時才載 | Live2D 這種「裝飾性、不影響主流程」的 |
worker | 在 web worker 跑(experimental) | 重 CPU 的分析 script |
Live2D 用 lazyOnload 是對的,不影響首屏 LCP。
防右鍵下載 / 拖曳
onContextMenu={(e) => e.preventDefault()} // 禁右鍵選單
onDragStart={(e) => e.preventDefault()} // 禁拖曳圖片
這只是「降低偷圖難度」阻止不了真心想偷的人(devtools 直接看 Network 抓貼圖檔)。但對 95% 一般使用者夠用。如果要更嚴密,要走 server-side 限制(token 驗證、低解析度截圖預覽)。
模型檔案結構
/public/live2d/
├── js/
│ └── live2d.js # wrapper script
└── model/
└── tia/
├── model.json # 模型描述檔(主入口)
├── tia.moc3 # 模型骨架
├── textures/
│ └── texture_00.png
├── motions/
│ ├── idle.motion3.json
│ └── tap.motion3.json
└── physics/
└── physics3.json
model.json 是入口,裡面索引所有相關檔案。loadlive2d(canvasId, jsonPath) 就是讀這個檔。
常見問題
1. canvas 內容糊掉
<canvas
id="live2d"
width="280" // ⭐ HTML attribute(描述繪圖解析度)
height="250" // ⭐ 不是 CSS height
style={{ width: '280px', height: '250px' }} // CSS 顯示尺寸
/>
width / height HTML attribute 控制畫布實際解析度,不是顯示尺寸。Retina 螢幕要用 2× 畫布解析度 顯示才不糊:
const dpr = window.devicePixelRatio || 1;
<canvas width={280 * dpr} height={250 * dpr} style={{ width: 280, height: 250 }} />
2. SSR 報錯
live2d.js 假設有 window,Next.js SSR 會炸。解法:
- 用
<Script>元件(只在 client 跑) - 或整個元件
dynamicimport 加ssr: false:
const Live2d = dynamic(() => import('./Live2d'), { ssr: false });
3. 多個 Live2D 同頁面衝突
loadlive2d 用 canvas id 識別,如果頁面有多個 Live2D,要用不同 id:
<canvas id="live2d-tia" />
<canvas id="live2d-mira" />
{`loadlive2d("live2d-tia", "/live2d/model/tia/model.json");`}
{`loadlive2d("live2d-mira", "/live2d/model/mira/model.json");`}
替代方案考量
| 方案 | 優 | 劣 |
|---|---|---|
| Live2D + galnetwen | 簡單、免費 | 老 SDK,API 受限 |
| pixi-live2d-display | 現代,基於 PixiJS | 設定較多 |
| VTube Studio Web | 商業級,功能完整 | 付費 |
| MMD on Web(Three.js) | 全 3D | 模型來源少 |
2026 年建議簡單展示用 galnetwen,需要互動 / 表情切換 / 嘴形同步用 pixi-live2d-display。