@seayoo-web/pixi-live2d 0.1.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,10 +4,10 @@
4
4
 
5
5
  ## 特性
6
6
 
7
- - **零 Vue 依赖**:纯 TypeScript 实现,可用于任何框架(React, Vue, Vanilla JS)。
8
- - **自动缩放**:内置适配 1920x1080 设计稿的响应式缩放逻辑。
9
- - **简单易用**:只需传入 Canvas 元素和模型路径即可。
10
- - **交互支持**:内置点击交互与动作播放方法。
7
+ - **零框架依赖**:纯 TypeScript 实现,可用于任何框架(ReactVueVanilla JS)。
8
+ - **多实例**:每个 `Live2DViewer` 持有独立的 PIXI 应用与 WebGL context,可在页面多处同时渲染、互不干扰。
9
+ - **响应式缩放**:按 canvas 容器尺寸适配设计稿(默认 1920×1080),并可选监听窗口 resize 自动调整。
10
+ - **交互支持**:内置点击播放动作、指针跟随等交互。
11
11
 
12
12
  ## 安装
13
13
 
@@ -15,92 +15,106 @@
15
15
  pnpm add @seayoo-web/pixi-live2d
16
16
  ```
17
17
 
18
- ## 在你的项目中使用:
18
+ ## 使用
19
19
 
20
20
  ### 基础用法
21
21
 
22
22
  ```typescript
23
23
  import { Live2DViewer } from "@seayoo-web/pixi-live2d";
24
24
 
25
- // 方式 1: 传入 Canvas ID
26
- const viewer1 = new Live2DViewer("live2d-canvas", {
25
+ // 方式 1:传入 canvas 元素的 id
26
+ const viewer = new Live2DViewer("live2d-canvas", {
27
27
  modelPath: "https://cdn.example.com/live2d/model.json",
28
28
  });
29
29
 
30
- // 方式 2: 传入 HTMLCanvasElement 并配置初始参数
31
- const canvasElement = document.getElementById("my-canvas") as HTMLCanvasElement;
32
- const viewer2 = new Live2DViewer(canvasElement, {
30
+ // 方式 2:传入 HTMLCanvasElement 并配置初始参数
31
+ const canvas = document.getElementById("my-canvas") as HTMLCanvasElement;
32
+ const viewer2 = new Live2DViewer(canvas, {
33
33
  modelPath: "https://cdn.example.com/live2d/model.json",
34
-
35
- // 初始位置和缩放
36
34
  x: 0,
37
35
  y: 0,
38
36
  scale: 0.2, // 模型缩放比例
39
-
40
- // 设计稿尺寸 (用于自动计算适配比例)
41
- designWidth: 1920,
37
+ designWidth: 1920, // 设计稿尺寸(用于自动计算适配比例)
42
38
  designHeight: 1080,
43
-
44
39
  autoResize: true,
45
-
46
- onModelLoaded: (model) => {
47
- console.log("模型加载成功:", model);
48
- },
49
- onModelError: (error) => {
50
- console.error("模型加载失败:", error);
51
- },
40
+ onModelLoaded: (model) => console.log("模型加载成功:", model),
41
+ onModelError: (error) => console.error("模型加载失败:", error),
52
42
  });
53
43
  ```
54
44
 
55
- ### API 说明
45
+ ### 多实例
56
46
 
57
- #### `new Live2DViewer(canvas, options)`
47
+ 每个实例使用各自的 canvas,可同时存在于页面不同位置、互不干扰:
58
48
 
59
- - `canvas`: `HTMLCanvasElement | string` - 用于渲染的 Canvas 元素或其 ID
60
- - `options`: `Live2DViewerOptions` - 配置项
49
+ ```typescript
50
+ const left = new Live2DViewer("canvas-left", { modelPath: "/a/model.json", scale: 0.2 });
51
+ const right = new Live2DViewer("canvas-right", { modelPath: "/b/model.json", scale: 0.2 });
61
52
 
62
- | 属性 | 类型 | 默认值 | 说明 |
63
- |Data Type|Type|Default|Description|
64
- |---|---|---|---|
65
- | `modelPath` | `string` | (必填) | Live2D 模型配置文件 (.json) 的路径 |
66
- | `scale` | `number` | - | 初始缩放比例 |
67
- | `x` | `number` | 0 | 初始 X 轴偏移 |
68
- | `y` | `number` | 0 | 初始 Y 轴偏移 |
69
- | `designWidth` | `number` | 1920 | 设计稿宽度(用于适配计算) |
70
- | `designHeight` | `number` | 1080 | 设计稿高度(用于适配计算) |
71
- | `autoResize` | `boolean` | `true` | 是否监听窗口 resize 事件并自动调整模型大小 |
72
- | `onModelLoaded` | `(model) => void` | - | 模型加载完成后的回调函数 |
73
- | `onModelError` | `(error) => void` | - | 模型加载失败的回调函数 |
74
-
75
- #### 实例方法
76
-
77
- - **`startMotion(group: string, no: number)`**
78
- 播放指定分组的动作。
53
+ // 卸载时分别销毁,各自释放 WebGL context
54
+ left.destroy();
55
+ right.destroy();
56
+ ```
79
57
 
80
- ```typescript
81
- viewer.startMotion("TapBody", 0);
82
- ```
58
+ > 浏览器对同页 WebGL context 数量有上限(通常约 16 个),请按需创建并在不用时调用 `destroy()`。
83
59
 
84
- - **`setScale(scale: number)`**
85
- 设置模型的缩放比例(基于自动计算的基础比例)。
60
+ ### 自定义 Cubism Core 地址
86
61
 
87
- ```typescript
88
- viewer.setScale(0.2);
89
- ```
62
+ 库默认从内置 CDN 加载 Live2D Cubism Core。可通过 `cubismCoreUrl` 覆盖,或在创建实例前手动预加载:
63
+
64
+ ```typescript
65
+ // 预加载(可选),多次调用会自动去重
66
+ await Live2DViewer.loadCubismCore("https://your-cdn.com/live2dcubismcore.js");
67
+
68
+ const viewer = new Live2DViewer("canvas", {
69
+ modelPath: "/model.json",
70
+ cubismCoreUrl: "https://your-cdn.com/live2dcubismcore.js",
71
+ });
72
+ ```
90
73
 
91
- - **`resize()`**
92
- 手动触发一次重新计算布局(通常在容器大小变化但窗口大小未变时使用)。
74
+ ## API
93
75
 
94
- ```typescript
95
- viewer.resize();
96
- ```
76
+ ### `new Live2DViewer(canvas, options)`
77
+
78
+ - `canvas`: `HTMLCanvasElement | string` —— 用于渲染的 canvas 元素或其 id
79
+ - `options`: `Live2DViewerOptions` —— 配置项
80
+
81
+ | 属性 | 类型 | 默认值 | 说明 |
82
+ | --- | --- | --- | --- |
83
+ | `modelPath` | `string` | (必填) | Live2D 模型配置文件 (.json) 的路径 |
84
+ | `scale` | `number` | `1` | 模型初始缩放比例 |
85
+ | `x` | `number` | `0` | 初始 X 轴偏移 |
86
+ | `y` | `number` | `0` | 初始 Y 轴偏移 |
87
+ | `designWidth` | `number` | `1920` | 设计稿宽度(用于适配计算) |
88
+ | `designHeight` | `number` | `1080` | 设计稿高度(用于适配计算) |
89
+ | `autoResize` | `boolean` | `true` | 是否监听窗口 resize 自动调整模型大小 |
90
+ | `interactive` | `boolean` | `true` | 是否响应指针移动进行跟随;设为 `false` 会同时禁用模型自然晃动 |
91
+ | `enableTapBody` | `boolean` | `true` | 是否在点击时触发 `TapBody` 动作 |
92
+ | `cubismCoreUrl` | `string` | 内置 CDN | Cubism Core 库的加载地址 |
93
+ | `onModelLoaded` | `(model) => void` | - | 模型加载完成后的回调 |
94
+ | `onModelError` | `(error: Error) => void` | - | 模型加载失败的回调 |
95
+
96
+ ### 实例方法
97
+
98
+ - **`startMotion(group, no, priority?, startTime?)`** —— 播放指定分组的动作。`priority` 默认 `1`;`startTime`(秒)默认 `0`,大于 0 时从该时间点开始播放。返回 `Promise<boolean>`。
97
99
 
98
- - **`destroy()`**
99
- 销毁实例,清理 PIXI 应用及事件监听。
100
100
  ```typescript
101
- viewer.destroy();
101
+ await viewer.startMotion("TapBody", 0);
102
102
  ```
103
103
 
104
+ - **`expression(name)`** —— 播放指定表情(名称或索引)。
105
+ - **`waitForMotionFinish()`** —— 返回 `Promise<boolean>`,当前动作播放结束后 resolve;无模型时立即 resolve `false`。
106
+ - **`setScale(scale)`** —— 设置模型缩放比例并刷新布局。
107
+ - **`setXY(x, y)`** —— 设置模型偏移并刷新布局。
108
+ - **`resize()`** —— 手动按当前 canvas 尺寸重新计算布局(容器尺寸变化但窗口未变时使用)。
109
+ - **`lockHeadAngle()`** —— 禁用模型的自然晃动(呼吸、身体/头部摇摆等)。注意:尽管命名含 head,实际会关闭**全部**自然动作。
110
+ - **`destroy()`** —— 销毁实例:移除监听、销毁模型,并释放该实例的 PIXI 应用与 WebGL context(不会移除你传入的 canvas)。销毁后该实例不可复用。
111
+
112
+ ### 静态方法
113
+
114
+ - **`Live2DViewer.loadCubismCore(url?)`** —— 预加载 Cubism Core 库,全局按需去重、只真正加载一次;加载失败会抛出明确错误。
115
+
104
116
  ## 注意事项
105
117
 
106
- 本库依赖 `pixi.js` v6 版本(与 `pixi-live2d-display` 兼容),请确保项目中未安装冲突的 PIXI 版本。
118
+ - 本库依赖 `pixi.js` v6(与 `pixi-live2d-display` 兼容),请确保项目中未安装冲突的 PIXI 版本。
119
+ - 模型适配以 **canvas 的显示尺寸** 为基准,请通过 CSS 给 canvas 设定期望的宽高。
120
+ - `interactive: false` 在禁用指针跟随的同时会关闭模型的自然晃动(等同调用 `lockHeadAngle()`)。
@@ -3211,7 +3211,7 @@ var Y = {
3211
3211
  }), e.IDENTITY = new e(), e;
3212
3212
  }();
3213
3213
  //#endregion
3214
- //#region ../../node_modules/.pnpm/@pixi+display@6.5.2_@pixi+math@6.5.2_@pixi+settings@6.5.2_@pixi+utils@6.5.2_@pixi+constants@6.5.2_@pixi+settings@6.5.2_/node_modules/@pixi/display/dist/esm/display.mjs
3214
+ //#region ../../node_modules/.pnpm/@pixi+display@6.5.2_@pixi+m_dbc4d2e5af9caaaf493b57ec65f40b11/node_modules/@pixi/display/dist/esm/display.mjs
3215
3215
  B.SORTABLE_CHILDREN = !1;
3216
3216
  var Vn = function() {
3217
3217
  function e() {
@@ -3804,7 +3804,7 @@ var gr = function(e) {
3804
3804
  }), e;
3805
3805
  }();
3806
3806
  //#endregion
3807
- //#region ../../node_modules/.pnpm/@pixi+ticker@6.5.2_@pixi+extensions@6.5.2_@pixi+settings@6.5.2/node_modules/@pixi/ticker/dist/esm/ticker.mjs
3807
+ //#region ../../node_modules/.pnpm/@pixi+ticker@6.5.2_@pixi+ex_40a8f4266f4966c790f91b30ddbc0f0c/node_modules/@pixi/ticker/dist/esm/ticker.mjs
3808
3808
  Object.defineProperties(X.prototype, {
3809
3809
  dispatch: { value: X.prototype.emit },
3810
3810
  run: { value: X.prototype.emit }
@@ -1,4 +1,4 @@
1
- import { Bt as e, Kt as t, Lt as n, Rt as r, dn as i, jt as a, nn as o, nt as s } from "./core-DiacFOEI.js";
1
+ import { Bt as e, Kt as t, Lt as n, Rt as r, dn as i, jt as a, nn as o, nt as s } from "./core-DBA9LYvt.js";
2
2
  //#region ../../node_modules/.pnpm/pixi-live2d-display@0.4.0_d39e0d9e955e28505b3b8642eca0c111/node_modules/pixi-live2d-display/dist/cubism4.es.js
3
3
  var c = Math.pow, l = (e, t, n) => new Promise((r, i) => {
4
4
  var a = (e) => {
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as e, $t as t, A as n, An as r, At as i, B as a, Bt as o, C as s, Cn as c, Ct as l, D as u, Dn as d, Dt as f, E as p, En as m, Et as h, F as g, Fn as _, Ft as v, G as y, Gt as b, H as x, Ht as S, I as C, In as w, It as T, J as E, Jt as D, K as O, Kt as k, L as A, Ln as j, Lt as M, M as N, Mn as P, Mt as F, N as ee, Nn as te, Nt as ne, O as re, On as ie, Ot as I, P as ae, Pn as oe, Pt as se, Q as L, Qt as R, R as ce, Rn as z, Rt as B, S as V, Sn as le, St as ue, T as de, Tn as fe, Tt as pe, U as me, Ut as H, V as he, Vt as ge, W as _e, Wt as ve, X as ye, Xt as be, Y as xe, Yt as Se, Z as Ce, Zt as we, _ as Te, _n as Ee, _t as De, a as Oe, an as ke, at as Ae, b as je, bn as Me, bt as Ne, c as U, cn as Pe, ct as Fe, d as Ie, dn as Le, dt as Re, en as ze, et as Be, f as Ve, fn as He, ft as Ue, g as We, gn as Ge, gt as Ke, h as qe, hn as Je, ht as Ye, i as Xe, in as Ze, it as Qe, j as $e, jn as W, jt as et, k as tt, kn as nt, kt as rt, l as it, ln as at, lt as ot, m as st, mn as ct, mt as lt, n as ut, nn as dt, nt as G, o as ft, on as pt, ot as mt, p as ht, pn as gt, pt as _t, q as vt, qt as yt, r as bt, rn as xt, rt as St, s as Ct, sn as wt, st as Tt, t as Et, tn as Dt, tt as Ot, u as kt, un as At, ut as jt, v as Mt, vn as Nt, vt as Pt, w as Ft, wn as It, wt as Lt, x as Rt, xn as K, xt as zt, y as Bt, yn as Vt, yt as Ht, z as Ut, zt as Wt } from "./core-DiacFOEI.js";
1
+ import { $ as e, $t as t, A as n, An as r, At as i, B as a, Bt as o, C as s, Cn as c, Ct as l, D as u, Dn as d, Dt as f, E as p, En as m, Et as h, F as g, Fn as _, Ft as v, G as y, Gt as b, H as x, Ht as S, I as C, In as w, It as T, J as E, Jt as D, K as O, Kt as k, L as A, Ln as j, Lt as M, M as N, Mn as P, Mt as F, N as ee, Nn as te, Nt as ne, O as re, On as ie, Ot as I, P as ae, Pn as oe, Pt as se, Q as L, Qt as R, R as ce, Rn as z, Rt as B, S as V, Sn as le, St as ue, T as de, Tn as fe, Tt as pe, U as me, Ut as H, V as he, Vt as ge, W as _e, Wt as ve, X as ye, Xt as be, Y as xe, Yt as Se, Z as Ce, Zt as we, _ as Te, _n as Ee, _t as De, a as Oe, an as ke, at as Ae, b as je, bn as Me, bt as Ne, c as U, cn as Pe, ct as Fe, d as Ie, dn as Le, dt as Re, en as ze, et as Be, f as Ve, fn as He, ft as Ue, g as We, gn as Ge, gt as Ke, h as qe, hn as Je, ht as Ye, i as Xe, in as Ze, it as Qe, j as $e, jn as W, jt as et, k as tt, kn as nt, kt as rt, l as it, ln as at, lt as ot, m as st, mn as ct, mt as lt, n as ut, nn as dt, nt as G, o as ft, on as pt, ot as mt, p as ht, pn as gt, pt as _t, q as vt, qt as yt, r as bt, rn as xt, rt as St, s as Ct, sn as wt, st as Tt, t as Et, tn as Dt, tt as Ot, u as kt, un as At, ut as jt, v as Mt, vn as Nt, vt as Pt, w as Ft, wn as It, wt as Lt, x as Rt, xn as K, xt as zt, y as Bt, yn as Vt, yt as Ht, z as Ut, zt as Wt } from "./core-DBA9LYvt.js";
2
2
  typeof globalThis < "u" || typeof self < "u" || typeof window < "u" || Function("return this")();
3
3
  async function Gt(e) {
4
4
  return await new Promise(function(t) {
@@ -277,7 +277,7 @@ globalThis.requestAnimationFrame || (globalThis.requestAnimationFrame = function
277
277
  return typeof e == "number" && isFinite(e) && Math.floor(e) === e;
278
278
  }), globalThis.ArrayBuffer || (globalThis.ArrayBuffer = Array), globalThis.Float32Array || (globalThis.Float32Array = Array), globalThis.Uint32Array || (globalThis.Uint32Array = Array), globalThis.Uint16Array || (globalThis.Uint16Array = Array), globalThis.Uint8Array || (globalThis.Uint8Array = Array), globalThis.Int32Array || (globalThis.Int32Array = Array);
279
279
  //#endregion
280
- //#region ../../node_modules/.pnpm/@pixi+accessibility@6.5.2_@pixi+core@6.5.2_@pixi+constants@6.5.2_@pixi+extensions@6.5.2_6fa672afef0d0e890b1f89be0296d21a/node_modules/@pixi/accessibility/dist/esm/accessibility.mjs
280
+ //#region ../../node_modules/.pnpm/@pixi+accessibility@6.5.2_@_6fa672afef0d0e890b1f89be0296d21a/node_modules/@pixi/accessibility/dist/esm/accessibility.mjs
281
281
  var mn = {
282
282
  accessible: !1,
283
283
  accessibleTitle: null,
@@ -1328,7 +1328,7 @@ var ur = function() {
1328
1328
  }();
1329
1329
  rt.add(or, ur);
1330
1330
  //#endregion
1331
- //#region ../../node_modules/.pnpm/@pixi+compressed-textures@6.5.2_@pixi+constants@6.5.2_@pixi+core@6.5.2_@pixi+constants@_e163e6679f0f4e58c3f6ee8bbe6275c0/node_modules/@pixi/compressed-textures/dist/esm/compressed-textures.mjs
1331
+ //#region ../../node_modules/.pnpm/@pixi+compressed-textures@6_e163e6679f0f4e58c3f6ee8bbe6275c0/node_modules/@pixi/compressed-textures/dist/esm/compressed-textures.mjs
1332
1332
  var Y, X;
1333
1333
  (function(e) {
1334
1334
  e[e.COMPRESSED_RGB_S3TC_DXT1_EXT = 33776] = "COMPRESSED_RGB_S3TC_DXT1_EXT", e[e.COMPRESSED_RGBA_S3TC_DXT1_EXT = 33777] = "COMPRESSED_RGBA_S3TC_DXT1_EXT", e[e.COMPRESSED_RGBA_S3TC_DXT3_EXT = 33778] = "COMPRESSED_RGBA_S3TC_DXT3_EXT", e[e.COMPRESSED_RGBA_S3TC_DXT5_EXT = 33779] = "COMPRESSED_RGBA_S3TC_DXT5_EXT", e[e.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT = 35917] = "COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT", e[e.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT = 35918] = "COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT", e[e.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT = 35919] = "COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT", e[e.COMPRESSED_SRGB_S3TC_DXT1_EXT = 35916] = "COMPRESSED_SRGB_S3TC_DXT1_EXT", e[e.COMPRESSED_R11_EAC = 37488] = "COMPRESSED_R11_EAC", e[e.COMPRESSED_SIGNED_R11_EAC = 37489] = "COMPRESSED_SIGNED_R11_EAC", e[e.COMPRESSED_RG11_EAC = 37490] = "COMPRESSED_RG11_EAC", e[e.COMPRESSED_SIGNED_RG11_EAC = 37491] = "COMPRESSED_SIGNED_RG11_EAC", e[e.COMPRESSED_RGB8_ETC2 = 37492] = "COMPRESSED_RGB8_ETC2", e[e.COMPRESSED_RGBA8_ETC2_EAC = 37496] = "COMPRESSED_RGBA8_ETC2_EAC", e[e.COMPRESSED_SRGB8_ETC2 = 37493] = "COMPRESSED_SRGB8_ETC2", e[e.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC = 37497] = "COMPRESSED_SRGB8_ALPHA8_ETC2_EAC", e[e.COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 37494] = "COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2", e[e.COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 37495] = "COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2", e[e.COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 35840] = "COMPRESSED_RGB_PVRTC_4BPPV1_IMG", e[e.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 35842] = "COMPRESSED_RGBA_PVRTC_4BPPV1_IMG", e[e.COMPRESSED_RGB_PVRTC_2BPPV1_IMG = 35841] = "COMPRESSED_RGB_PVRTC_2BPPV1_IMG", e[e.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG = 35843] = "COMPRESSED_RGBA_PVRTC_2BPPV1_IMG", e[e.COMPRESSED_RGB_ETC1_WEBGL = 36196] = "COMPRESSED_RGB_ETC1_WEBGL", e[e.COMPRESSED_RGB_ATC_WEBGL = 35986] = "COMPRESSED_RGB_ATC_WEBGL", e[e.COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL = 35986] = "COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL", e[e.COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL = 34798] = "COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL";
@@ -5491,7 +5491,7 @@ var $o = "varying vec2 vTextureCoord;\nuniform sampler2D uSampler;\nuniform floa
5491
5491
  }(Rt);
5492
5492
  es.prototype.grayscale = es.prototype.greyscale;
5493
5493
  //#endregion
5494
- //#region ../../node_modules/.pnpm/@pixi+filter-displacement@6.5.2_@pixi+core@6.5.2_@pixi+constants@6.5.2_@pixi+extensions_6c8bdb756a202d74aaa77fd3eb3cd558/node_modules/@pixi/filter-displacement/dist/esm/filter-displacement.mjs
5494
+ //#region ../../node_modules/.pnpm/@pixi+filter-displacement@6_6c8bdb756a202d74aaa77fd3eb3cd558/node_modules/@pixi/filter-displacement/dist/esm/filter-displacement.mjs
5495
5495
  var ts = function(e, t) {
5496
5496
  return ts = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function(e, t) {
5497
5497
  e.__proto__ = t;
@@ -5691,7 +5691,7 @@ var Is = function() {
5691
5691
  return e;
5692
5692
  }();
5693
5693
  //#endregion
5694
- //#region ../../node_modules/.pnpm/@pixi+mixin-get-global-position@6.5.2_@pixi+display@6.5.2_@pixi+math@6.5.2_@pixi+settin_028b3c491a57204327251ef324f60475/node_modules/@pixi/mixin-get-global-position/dist/esm/mixin-get-global-position.mjs
5694
+ //#region ../../node_modules/.pnpm/@pixi+mixin-get-global-posi_028b3c491a57204327251ef324f60475/node_modules/@pixi/mixin-get-global-position/dist/esm/mixin-get-global-position.mjs
5695
5695
  Object.defineProperties(F.prototype, {
5696
5696
  cacheAsBitmapResolution: {
5697
5697
  get: function() {
@@ -5795,7 +5795,7 @@ Object.defineProperties(F.prototype, {
5795
5795
  return e === void 0 && (e = new o()), t === void 0 && (t = !1), this.parent ? this.parent.toGlobal(this.position, e, t) : (e.x = this.position.x, e.y = this.position.y), e;
5796
5796
  };
5797
5797
  //#endregion
5798
- //#region ../../node_modules/.pnpm/@pixi+app@6.5.2_@pixi+core@6.5.2_@pixi+constants@6.5.2_@pixi+extensions@6.5.2_@pixi+mat_d997c290ef5e4553ddd5c6768aff9f1c/node_modules/@pixi/app/dist/esm/app.mjs
5798
+ //#region ../../node_modules/.pnpm/@pixi+app@6.5.2_@pixi+core@_d997c290ef5e4553ddd5c6768aff9f1c/node_modules/@pixi/app/dist/esm/app.mjs
5799
5799
  var Ls = function() {
5800
5800
  function e() {}
5801
5801
  return e.init = function(e) {
@@ -6428,7 +6428,7 @@ var nl = function() {
6428
6428
  }();
6429
6429
  rt.handleByList(I.Application, nl._plugins), rt.add(Ls);
6430
6430
  //#endregion
6431
- //#region ../../node_modules/.pnpm/@pixi+mesh-extras@6.5.2_@pixi+constants@6.5.2_@pixi+core@6.5.2_@pixi+constants@6.5.2_@p_2c67296e184f7a20c4c1bea5a2db14c2/node_modules/@pixi/mesh-extras/dist/esm/mesh-extras.mjs
6431
+ //#region ../../node_modules/.pnpm/@pixi+mesh-extras@6.5.2_@pi_2c67296e184f7a20c4c1bea5a2db14c2/node_modules/@pixi/mesh-extras/dist/esm/mesh-extras.mjs
6432
6432
  var rl = function(e, t) {
6433
6433
  return rl = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function(e, t) {
6434
6434
  e.__proto__ = t;
@@ -6958,48 +6958,45 @@ var gl = {
6958
6958
  DisplacementFilter: as,
6959
6959
  FXAAFilter: us,
6960
6960
  NoiseFilter: ms
6961
- }, _l = "https://a0.seayooassets.com/web/libs/live2dcubismcore.js", vl = Gt(_l), yl = vl.then(() => import("./cubism4.es-B18JJVJM.js")), bl = class e {
6962
- static sharedApp = null;
6961
+ }, _l = "https://a0.seayooassets.com/web/libs/live2dcubismcore.js", vl = null;
6962
+ function yl() {
6963
+ return vl ||= import("./cubism4.es-DbGCrqOi.js"), vl;
6964
+ }
6965
+ var bl = class e {
6963
6966
  static coreLoadingPromise = null;
6964
6967
  static async loadCubismCore(e = _l) {
6965
- this.coreLoadingPromise ||= Gt(e), await this.coreLoadingPromise;
6968
+ if (this.coreLoadingPromise ||= Gt(e), !await this.coreLoadingPromise) throw this.coreLoadingPromise = null, Error(`Failed to load Live2D Cubism Core from "${e}"`);
6966
6969
  }
6967
- static loadingToken = 0;
6968
- static hasWarmedUp = !1;
6969
6970
  app;
6970
6971
  model = null;
6971
6972
  options;
6972
- constructor(t, n) {
6973
- this.options = n, window.PIXI || (window.PIXI = hl);
6974
- let r;
6975
- if (typeof t == "string") {
6976
- let e = document.getElementById(t);
6977
- if (!e) throw Error(`Canvas element with id "${t}" not found.`);
6978
- if (!(e instanceof HTMLCanvasElement)) throw Error(`Element with id "${t}" is not a canvas.`);
6979
- r = e;
6980
- } else r = t;
6981
- if (!e.sharedApp) {
6982
- let t = window.devicePixelRatio || 1, n = r.getBoundingClientRect(), i = Math.max(n.width, 100), a = Math.max(n.height, 100);
6983
- r.width = i * t, r.height = a * t, e.sharedApp = new nl({
6984
- view: r,
6985
- resizeTo: r,
6986
- backgroundAlpha: 0,
6987
- autoStart: !0,
6988
- sharedTicker: !0,
6989
- resolution: t
6990
- }), e.sharedApp.renderer.resolution = t;
6991
- } else if (e.sharedApp.view !== r) {
6992
- let t = r.parentElement, n = e.sharedApp.view;
6993
- t && (n.className = r.className, n.style.cssText = r.style.cssText, r.id && (n.id = r.id), t.replaceChild(n, r));
6994
- }
6995
- this.app = e.sharedApp, this.init(), this.options.autoResize !== !1 && window.addEventListener("resize", this.handleResize);
6973
+ loadingToken = 0;
6974
+ hasWarmedUp = !1;
6975
+ constructor(e, t) {
6976
+ this.options = t, window.PIXI || (window.PIXI = hl);
6977
+ let n;
6978
+ if (typeof e == "string") {
6979
+ let t = document.getElementById(e);
6980
+ if (!t) throw Error(`Canvas element with id "${e}" not found.`);
6981
+ if (!(t instanceof HTMLCanvasElement)) throw Error(`Element with id "${e}" is not a canvas.`);
6982
+ n = t;
6983
+ } else n = e;
6984
+ let r = window.devicePixelRatio || 1, i = n.getBoundingClientRect(), a = Math.max(i.width, 100), o = Math.max(i.height, 100);
6985
+ n.width = a * r, n.height = o * r, this.app = new nl({
6986
+ view: n,
6987
+ resizeTo: n,
6988
+ backgroundAlpha: 0,
6989
+ autoStart: !0,
6990
+ sharedTicker: !0,
6991
+ resolution: r
6992
+ }), this.init(), this.options.autoResize !== !1 && window.addEventListener("resize", this.handleResize);
6996
6993
  }
6997
6994
  async init() {
6998
6995
  try {
6999
- let t = ++e.loadingToken;
6996
+ let t = ++this.loadingToken;
7000
6997
  await e.loadCubismCore(this.options.cubismCoreUrl);
7001
- let { Live2DModel: n } = await yl, r = await n.from(this.options.modelPath);
7002
- if (t !== e.loadingToken) {
6998
+ let { Live2DModel: n } = await yl(), r = await n.from(this.options.modelPath);
6999
+ if (t !== this.loadingToken) {
7003
7000
  r.destroy({
7004
7001
  children: !0,
7005
7002
  texture: !0,
@@ -7007,8 +7004,8 @@ var gl = {
7007
7004
  });
7008
7005
  return;
7009
7006
  }
7010
- if (!e.hasWarmedUp) {
7011
- e.hasWarmedUp = !0, this.app.stage.addChild(r);
7007
+ if (!this.hasWarmedUp) {
7008
+ this.hasWarmedUp = !0, this.app.stage.addChild(r);
7012
7009
  try {
7013
7010
  this.app.renderer.render(this.app.stage);
7014
7011
  } catch {}
@@ -7016,8 +7013,8 @@ var gl = {
7016
7013
  children: !0,
7017
7014
  texture: !0,
7018
7015
  baseTexture: !0
7019
- }), await new Promise((e) => requestAnimationFrame(() => e())), t !== e.loadingToken) return;
7020
- if (r = await n.from(this.options.modelPath), t !== e.loadingToken) {
7016
+ }), await new Promise((e) => requestAnimationFrame(() => e())), t !== this.loadingToken) return;
7017
+ if (r = await n.from(this.options.modelPath), t !== this.loadingToken) {
7021
7018
  r.destroy({
7022
7019
  children: !0,
7023
7020
  texture: !0,
@@ -7054,8 +7051,10 @@ var gl = {
7054
7051
  };
7055
7052
  resize() {
7056
7053
  if (!this.model || !this.app) return;
7057
- let e = window.innerWidth / (this.options.designWidth || 1920), t = window.innerHeight / (this.options.designHeight || 1080), n = Math.min(e, t);
7058
- this.app.stage.scale.set(n, n), this.model.scale.set(this.options.scale, this.options.scale), this.model.x = (this.options.x || 0) * n, this.model.y = (this.options.y || 0) * n;
7054
+ let e = this.app.view.getBoundingClientRect(), t = e.width || window.innerWidth, n = e.height || window.innerHeight, r = t / (this.options.designWidth || 1920), i = n / (this.options.designHeight || 1080), a = Math.min(r, i);
7055
+ this.app.stage.scale.set(a, a);
7056
+ let o = this.options.scale ?? 1;
7057
+ this.model.scale.set(o, o), this.model.x = (this.options.x || 0) * a, this.model.y = (this.options.y || 0) * a;
7059
7058
  }
7060
7059
  setScale(e) {
7061
7060
  this.options.scale = e, this.resize();
@@ -7083,24 +7082,30 @@ var gl = {
7083
7082
  }
7084
7083
  waitForMotionFinish() {
7085
7084
  return new Promise((e) => {
7086
- let t = () => {
7087
- if (!this.model) return !1;
7088
- this.model.internalModel.motionManager.off("motionFinish", t), e(!0);
7085
+ if (!this.model) {
7086
+ e(!1);
7087
+ return;
7088
+ }
7089
+ let t = this.model.internalModel.motionManager, n = () => {
7090
+ t.off("motionFinish", n), e(!0);
7089
7091
  };
7090
- if (!this.model) return !1;
7091
- this.model.internalModel.motionManager.on("motionFinish", t);
7092
+ t.on("motionFinish", n);
7092
7093
  });
7093
7094
  }
7094
7095
  destroy() {
7095
- this.options.autoResize !== !1 && window.removeEventListener("resize", this.handleResize), this.model &&= (this.app.stage.removeChild(this.model), this.model.destroy({
7096
+ this.options.autoResize !== !1 && window.removeEventListener("resize", this.handleResize), this.loadingToken++, this.model &&= (this.app.stage.removeChild(this.model), this.model.destroy({
7096
7097
  children: !0,
7097
7098
  texture: !0,
7098
7099
  baseTexture: !0
7099
- }), null);
7100
+ }), null), this.app.destroy(!1, {
7101
+ children: !0,
7102
+ texture: !0,
7103
+ baseTexture: !0
7104
+ });
7100
7105
  }
7101
7106
  };
7102
7107
  function xl(e) {
7103
7108
  return e instanceof Error ? e : Error(String(e));
7104
7109
  }
7105
7110
  //#endregion
7106
- export { bl as Live2DViewer, vl as coreLoadPromise };
7111
+ export { bl as Live2DViewer };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seayoo-web/pixi-live2d",
3
- "version": "0.1.1",
3
+ "version": "1.0.0",
4
4
  "description": "pixi-live2d viewer",
5
5
  "keywords": [
6
6
  "pixi-live2d"
@@ -39,18 +39,20 @@
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/node": "^22.13.1",
42
- "@seayoo-web/tsconfig": "^1.0.6",
43
- "@seayoo-web/utils": "^4.5.2"
42
+ "@vitest/coverage-istanbul": "^4.1.4",
43
+ "happy-dom": "^13.10.1",
44
+ "vitest": "^4.1.4",
45
+ "@seayoo-web/utils": "^4.5.4",
46
+ "@seayoo-web/tsconfig": "^1.0.6"
44
47
  },
45
48
  "engines": {
46
49
  "node": ">=22"
47
50
  },
48
51
  "scripts": {
49
- "build": "vite build && tsc --build --force --emitDeclarationOnly",
52
+ "build": "vite build && tsc --build --emitDeclarationOnly",
50
53
  "type-check": "tsc --noEmit",
51
54
  "lint": "oxlint -c ../../oxlint.config.mjs",
52
55
  "lint:fix": "oxlint -c ../../oxlint.config.mjs --fix",
53
- "prepublish": "pnpm lint:fix && pnpm build",
54
56
  "test": "vitest --dom",
55
57
  "coverage": "vitest run --coverage"
56
58
  }
package/types/index.d.ts CHANGED
@@ -1,6 +1,4 @@
1
1
  import * as PIXI from "pixi.js";
2
- declare const coreLoadPromise: Promise<boolean>;
3
- export { coreLoadPromise };
4
2
  import type { InternalModel as InternalModelType, Live2DModel as Live2DModelType } from "pixi-live2d-display/cubism4";
5
3
  export type { MotionPriority } from "pixi-live2d-display/cubism4";
6
4
  declare global {
@@ -36,8 +34,6 @@ export interface Live2DViewerOptions {
36
34
  onModelError?: (error: Error) => void;
37
35
  }
38
36
  export declare class Live2DViewer {
39
- /** 单例 PIXI 应用 */
40
- static sharedApp: PIXI.Application | null;
41
37
  /** 引擎加载状态 Promise,用于确保全局只加载一次 Core 库 */
42
38
  private static coreLoadingPromise;
43
39
  /**
@@ -45,16 +41,16 @@ export declare class Live2DViewer {
45
41
  * @param url - Core 库的 URL 地址
46
42
  */
47
43
  static loadCubismCore(url?: string): Promise<void>;
48
- /** 最新发起的加载任务 token,防频繁加载的竞态泄漏 */
49
- private static loadingToken;
50
- /** 整个页面生命周期内 Cubism 渲染管线是否已完成首次预热 */
51
- private static hasWarmedUp;
52
- /** PIXI 应用实例 */
44
+ /** PIXI 应用实例(每个 viewer 独立,持有各自的 canvas / stage / WebGL context) */
53
45
  app: PIXI.Application;
54
46
  /** Live2D 模型实例 */
55
47
  model: Live2DModelType<InternalModelType> | null;
56
- /** 配置项副本 */
48
+ /** 配置项 */
57
49
  private options;
50
+ /** 最新发起的加载任务 token,防同一实例内频繁加载的竞态泄漏 */
51
+ private loadingToken;
52
+ /** 该实例(WebGL context)的渲染管线是否已完成首次预热 */
53
+ private hasWarmedUp;
58
54
  /**
59
55
  * 创建 Live2D 查看器实例
60
56
  * @param canvas - 用于渲染的 Canvas 元素或其 ID
@@ -66,8 +62,10 @@ export declare class Live2DViewer {
66
62
  /** 设置点击交互 */
67
63
  private setupInteraction;
68
64
  /**
69
- * 锁定头部角度参数,防止 motion 驱动头部晃动
70
- * 在 ticker 里 motion 更新之后强制覆盖为 0
65
+ * 禁用模型的自然晃动(呼吸、身体/头部摇摆等)。
66
+ *
67
+ * 实现方式是把 internalModel 的 updateNaturalMovements 覆写为空操作。
68
+ * 注意:尽管方法名含 head,实际会关闭**全部**自然动作,而不仅是头部角度。
71
69
  */
72
70
  lockHeadAngle(): void;
73
71
  /** 窗口大小改变的处理函数 */