@vmz/plugin-live2d 0.0.21 → 0.0.23
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/components/Live2dHost.vmz +68 -24
- package/package.json +2 -2
|
@@ -55,8 +55,12 @@ export default class Live2dHost {
|
|
|
55
55
|
public capturePresetId = '';
|
|
56
56
|
/** Called with Live2DRuntime after loadModel succeeds. */
|
|
57
57
|
public onReady = null;
|
|
58
|
-
/** Optional frame profile listener. */
|
|
58
|
+
/** Optional frame profile listener (caller should throttle UI). */
|
|
59
59
|
public onProfile = null;
|
|
60
|
+
/** Load / mount failure. */
|
|
61
|
+
public onError = null;
|
|
62
|
+
/** Pointer hit area string (often `drawable:N`). */
|
|
63
|
+
public onHit = null;
|
|
60
64
|
|
|
61
65
|
hostId = `live2d-host-${++hostSeq}`;
|
|
62
66
|
hostStyle = '';
|
|
@@ -64,9 +68,10 @@ export default class Live2dHost {
|
|
|
64
68
|
progressPercent = 0;
|
|
65
69
|
progressLabel = 'Loading…';
|
|
66
70
|
runtime = null;
|
|
67
|
-
_raf = 0;
|
|
68
|
-
_lastTs = 0;
|
|
69
71
|
_unsubProfile = null;
|
|
72
|
+
_canvas = null;
|
|
73
|
+
_boundPointer = null;
|
|
74
|
+
_bootGeneration = 0;
|
|
70
75
|
|
|
71
76
|
onMount() {
|
|
72
77
|
this.hostStyle = `width:${this.width}px;height:${this.height}px`;
|
|
@@ -86,27 +91,44 @@ export default class Live2dHost {
|
|
|
86
91
|
}
|
|
87
92
|
|
|
88
93
|
teardownRuntime() {
|
|
89
|
-
|
|
94
|
+
try {
|
|
95
|
+
this.runtime?.stage?.stop?.();
|
|
96
|
+
} catch {
|
|
97
|
+
/* updateMode may be manual on older callers */
|
|
98
|
+
}
|
|
99
|
+
this.detachPointer();
|
|
90
100
|
if (typeof this._unsubProfile === 'function') {
|
|
91
101
|
this._unsubProfile();
|
|
92
102
|
this._unsubProfile = null;
|
|
93
103
|
}
|
|
94
104
|
this.runtime?.destroy?.();
|
|
95
105
|
this.runtime = null;
|
|
106
|
+
this._canvas = null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
detachPointer() {
|
|
110
|
+
if (this._canvas && this._boundPointer) {
|
|
111
|
+
this._canvas.removeEventListener('pointerdown', this._boundPointer);
|
|
112
|
+
}
|
|
113
|
+
this._boundPointer = null;
|
|
96
114
|
}
|
|
97
115
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
this.
|
|
101
|
-
this.
|
|
116
|
+
attachPointer(canvas) {
|
|
117
|
+
this.detachPointer();
|
|
118
|
+
this._boundPointer = (event) => this.onPointerDown(event);
|
|
119
|
+
canvas.addEventListener('pointerdown', this._boundPointer);
|
|
102
120
|
}
|
|
103
121
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
this.
|
|
108
|
-
|
|
109
|
-
|
|
122
|
+
onPointerDown(event) {
|
|
123
|
+
const runtime = this.runtime;
|
|
124
|
+
const canvas = this._canvas;
|
|
125
|
+
if (!runtime || !canvas || typeof this.onHit !== 'function') return;
|
|
126
|
+
const rect = canvas.getBoundingClientRect();
|
|
127
|
+
if (rect.width <= 0 || rect.height <= 0) return;
|
|
128
|
+
const nx = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
|
129
|
+
const ny = 1 - ((event.clientY - rect.top) / rect.height) * 2;
|
|
130
|
+
const area = runtime.hitTest?.(nx, ny) ?? null;
|
|
131
|
+
this.onHit(area);
|
|
110
132
|
}
|
|
111
133
|
|
|
112
134
|
ensureCanvas() {
|
|
@@ -120,6 +142,7 @@ export default class Live2dHost {
|
|
|
120
142
|
canvas.style.width = `${this.width}px`;
|
|
121
143
|
canvas.style.height = `${this.height}px`;
|
|
122
144
|
host.appendChild(canvas);
|
|
145
|
+
this._canvas = canvas;
|
|
123
146
|
return canvas;
|
|
124
147
|
}
|
|
125
148
|
|
|
@@ -132,48 +155,69 @@ export default class Live2dHost {
|
|
|
132
155
|
return this.runtime.capturePng(opts);
|
|
133
156
|
}
|
|
134
157
|
|
|
158
|
+
reportError(err) {
|
|
159
|
+
this.loading = false;
|
|
160
|
+
const message = String(err?.message || err || 'load error');
|
|
161
|
+
if (this.exposeCaptureApi && typeof window !== 'undefined') {
|
|
162
|
+
const api = window.__LIVE2D_CAPTURE__;
|
|
163
|
+
if (api) api.error = message;
|
|
164
|
+
}
|
|
165
|
+
if (typeof this.onError === 'function') this.onError(message);
|
|
166
|
+
}
|
|
167
|
+
|
|
135
168
|
async boot() {
|
|
136
169
|
if (!this.model) return;
|
|
170
|
+
const generation = ++this._bootGeneration;
|
|
137
171
|
this.loading = true;
|
|
138
172
|
this.progressPercent = 0;
|
|
139
173
|
this.progressLabel = 'loading';
|
|
140
174
|
try {
|
|
141
175
|
this.teardownRuntime();
|
|
176
|
+
if (generation !== this._bootGeneration) return;
|
|
142
177
|
const canvas = this.ensureCanvas();
|
|
143
178
|
if (!canvas) return;
|
|
144
179
|
const prefer = Array.isArray(this.prefer)
|
|
145
180
|
? this.prefer
|
|
146
181
|
: ['canvas2d', 'webgl2', 'webgpu'];
|
|
147
|
-
const runtime = createLive2D({ prefer, updateMode: '
|
|
182
|
+
const runtime = createLive2D({ prefer, updateMode: 'auto' });
|
|
148
183
|
runtime.events.on('ready', () => {
|
|
184
|
+
if (generation !== this._bootGeneration) return;
|
|
149
185
|
this.loading = false;
|
|
150
186
|
this.progressPercent = 100;
|
|
151
187
|
this.progressLabel = 'ready';
|
|
152
188
|
if (this.autoplay) {
|
|
153
|
-
|
|
154
|
-
|
|
189
|
+
try {
|
|
190
|
+
runtime.stage.start();
|
|
191
|
+
} catch {
|
|
192
|
+
/* already running */
|
|
193
|
+
}
|
|
155
194
|
}
|
|
195
|
+
this.attachPointer(canvas);
|
|
156
196
|
if (this.exposeCaptureApi) void this.publishCapture();
|
|
157
197
|
if (typeof this.onReady === 'function') this.onReady(runtime);
|
|
158
198
|
});
|
|
159
199
|
runtime.events.on('error', (p) => {
|
|
160
|
-
this.
|
|
161
|
-
|
|
162
|
-
const api = window.__LIVE2D_CAPTURE__;
|
|
163
|
-
if (api) api.error = String(p?.error || 'load error');
|
|
164
|
-
}
|
|
200
|
+
if (generation !== this._bootGeneration) return;
|
|
201
|
+
this.reportError(p?.error || 'load error');
|
|
165
202
|
});
|
|
166
203
|
if (typeof this.onProfile === 'function') {
|
|
167
204
|
this._unsubProfile = runtime.events.on('profile', (profile) => {
|
|
205
|
+
if (generation !== this._bootGeneration) return;
|
|
168
206
|
this.onProfile(profile);
|
|
169
207
|
});
|
|
170
208
|
}
|
|
171
209
|
runtime.mount(canvas);
|
|
172
210
|
this.progressPercent = 35;
|
|
173
211
|
await runtime.loadModel(this.model);
|
|
212
|
+
if (generation !== this._bootGeneration) {
|
|
213
|
+
runtime.destroy?.();
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
174
216
|
this.runtime = runtime;
|
|
175
|
-
|
|
176
|
-
|
|
217
|
+
this.progressPercent = 90;
|
|
218
|
+
} catch (err) {
|
|
219
|
+
if (generation !== this._bootGeneration) return;
|
|
220
|
+
this.reportError(err);
|
|
177
221
|
}
|
|
178
222
|
}
|
|
179
223
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vmz/plugin-live2d",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.23",
|
|
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.
|
|
39
|
+
"@doki-land/live2d": "0.0.23",
|
|
40
40
|
"@vmz/plugin": "^0.1.8"
|
|
41
41
|
}
|
|
42
42
|
}
|