@vmz/plugin-live2d 0.0.20 → 0.0.21

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.
@@ -1,20 +1,40 @@
1
1
  <template>
2
2
  <div class="live2d-host" style={hostStyle}>
3
3
  <div class="live2d-host__mount" id={hostId}></div>
4
- <div if={showProgress && loading} class="live2d-host__progress" role="progressbar" aria-valuenow={progressPercent} aria-valuemin="0" aria-valuemax="100">
4
+ <div
5
+ if={showProgress && loading}
6
+ class="live2d-host__progress"
7
+ role="progressbar"
8
+ aria-valuenow={progressPercent}
9
+ aria-valuemin="0"
10
+ aria-valuemax="100"
11
+ >
5
12
  <span>{progressLabel} · {progressPercent}%</span>
6
13
  </div>
7
14
  </div>
8
15
  </template>
9
16
 
10
17
  <style>
11
- .live2d-host { position: relative; display: inline-block; }
12
- .live2d-host__mount { width: 100%; height: 100%; }
13
- .live2d-host__mount canvas { display: block; width: 100%; height: 100%; }
18
+ .live2d-host {
19
+ position: relative;
20
+ display: inline-block;
21
+ }
22
+ .live2d-host__mount {
23
+ width: 100%;
24
+ height: 100%;
25
+ }
26
+ .live2d-host__mount canvas {
27
+ display: block;
28
+ width: 100%;
29
+ height: 100%;
30
+ }
14
31
  .live2d-host__progress {
15
- position: absolute; inset: auto 0 0; padding: 0.35rem 0.5rem;
32
+ position: absolute;
33
+ inset: auto 0 0;
34
+ padding: 0.35rem 0.5rem;
16
35
  background: rgba(255, 255, 255, 0.92);
17
- font: 600 0.75rem/1.2 ui-monospace, monospace; color: #377ba8;
36
+ font: 600 0.75rem/1.2 ui-monospace, monospace;
37
+ color: #377ba8;
18
38
  }
19
39
  </style>
20
40
 
@@ -33,6 +53,10 @@ export default class Live2dHost {
33
53
  public showProgress = true;
34
54
  public exposeCaptureApi = false;
35
55
  public capturePresetId = '';
56
+ /** Called with Live2DRuntime after loadModel succeeds. */
57
+ public onReady = null;
58
+ /** Optional frame profile listener. */
59
+ public onProfile = null;
36
60
 
37
61
  hostId = `live2d-host-${++hostSeq}`;
38
62
  hostStyle = '';
@@ -42,6 +66,7 @@ export default class Live2dHost {
42
66
  runtime = null;
43
67
  _raf = 0;
44
68
  _lastTs = 0;
69
+ _unsubProfile = null;
45
70
 
46
71
  onMount() {
47
72
  this.hostStyle = `width:${this.width}px;height:${this.height}px`;
@@ -57,8 +82,16 @@ export default class Live2dHost {
57
82
  }
58
83
 
59
84
  onDestroy() {
85
+ this.teardownRuntime();
86
+ }
87
+
88
+ teardownRuntime() {
60
89
  this.stopLoop();
61
- void this.runtime?.destroy?.();
90
+ if (typeof this._unsubProfile === 'function') {
91
+ this._unsubProfile();
92
+ this._unsubProfile = null;
93
+ }
94
+ this.runtime?.destroy?.();
62
95
  this.runtime = null;
63
96
  }
64
97
 
@@ -90,6 +123,10 @@ export default class Live2dHost {
90
123
  return canvas;
91
124
  }
92
125
 
126
+ getRuntime() {
127
+ return this.runtime;
128
+ }
129
+
93
130
  async capturePng(opts = {}) {
94
131
  if (!this.runtime) throw new Error('Live2dHost: runtime not ready');
95
132
  return this.runtime.capturePng(opts);
@@ -98,20 +135,26 @@ export default class Live2dHost {
98
135
  async boot() {
99
136
  if (!this.model) return;
100
137
  this.loading = true;
138
+ this.progressPercent = 0;
139
+ this.progressLabel = 'loading';
101
140
  try {
102
- this.stopLoop();
103
- void this.runtime?.destroy?.();
141
+ this.teardownRuntime();
104
142
  const canvas = this.ensureCanvas();
105
143
  if (!canvas) return;
106
- const runtime = createLive2D({ prefer: this.prefer, updateMode: 'manual' });
107
- runtime.events.on('progress', (p) => this.onProgress(p));
144
+ const prefer = Array.isArray(this.prefer)
145
+ ? this.prefer
146
+ : ['canvas2d', 'webgl2', 'webgpu'];
147
+ const runtime = createLive2D({ prefer, updateMode: 'manual' });
108
148
  runtime.events.on('ready', () => {
109
149
  this.loading = false;
150
+ this.progressPercent = 100;
151
+ this.progressLabel = 'ready';
110
152
  if (this.autoplay) {
111
153
  this._lastTs = 0;
112
154
  this._raf = requestAnimationFrame((t) => this.tick(t));
113
155
  }
114
156
  if (this.exposeCaptureApi) void this.publishCapture();
157
+ if (typeof this.onReady === 'function') this.onReady(runtime);
115
158
  });
116
159
  runtime.events.on('error', (p) => {
117
160
  this.loading = false;
@@ -120,20 +163,20 @@ export default class Live2dHost {
120
163
  if (api) api.error = String(p?.error || 'load error');
121
164
  }
122
165
  });
123
- await runtime.mount(canvas);
124
- await runtime.load(this.model);
166
+ if (typeof this.onProfile === 'function') {
167
+ this._unsubProfile = runtime.events.on('profile', (profile) => {
168
+ this.onProfile(profile);
169
+ });
170
+ }
171
+ runtime.mount(canvas);
172
+ this.progressPercent = 35;
173
+ await runtime.loadModel(this.model);
125
174
  this.runtime = runtime;
126
175
  } catch {
127
176
  this.loading = false;
128
177
  }
129
178
  }
130
179
 
131
- onProgress(p) {
132
- const progress = p?.progress ?? 0;
133
- this.progressLabel = p?.stage || 'loading';
134
- this.progressPercent = Math.round(Math.min(1, Math.max(0, progress)) * 100);
135
- }
136
-
137
180
  async publishCapture() {
138
181
  const api = typeof window !== 'undefined' ? window.__LIVE2D_CAPTURE__ : null;
139
182
  if (!api) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vmz/plugin-live2d",
3
- "version": "0.0.20",
3
+ "version": "0.0.21",
4
4
  "description": "VMZ Live2D adapter — <Live2dHost> for @doki-land/live2d in VMZ applications",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -36,7 +36,7 @@
36
36
  "test": "vitest run --passWithNoTests"
37
37
  },
38
38
  "dependencies": {
39
- "@doki-land/live2d": "0.0.20",
39
+ "@doki-land/live2d": "0.0.21",
40
40
  "@vmz/plugin": "^0.1.8"
41
41
  }
42
42
  }