be1e51cc9a
Eliminates WebGL bindBuffer/bindTexture spam from dual Application contexts. Cat model now loads onto KiraAvatar's shared stage via onAppReady callback.
60 lines
1.5 KiB
TypeScript
60 lines
1.5 KiB
TypeScript
import { useEffect, useRef } from 'react';
|
|
|
|
interface Props {
|
|
app: any;
|
|
className?: string;
|
|
}
|
|
|
|
export default function Live2DCat({ app, className }: Props) {
|
|
const modelRef = useRef<any>(null);
|
|
|
|
useEffect(() => {
|
|
if (!app) return;
|
|
let mounted = true;
|
|
|
|
const init = async () => {
|
|
try {
|
|
const { Live2DModel } = await import('pixi-live2d-display/cubism4');
|
|
|
|
const model = await Live2DModel.from('/live2d/models/little-cat/LittleCat.model3.json', {
|
|
autoInteract: false,
|
|
});
|
|
if (!mounted) return;
|
|
|
|
// Scale to fit ~120x120 area on the shared stage
|
|
const sw = app.screen.width;
|
|
const sh = app.screen.height;
|
|
const targetW = 120;
|
|
const targetH = 120;
|
|
const s = Math.min(targetW / model.width, targetH / model.height);
|
|
model.scale.set(s);
|
|
model.anchor.set(0.5, 0.5);
|
|
// Position near top-left corner with some padding
|
|
model.position.set(80, 80);
|
|
|
|
app.stage.addChild(model as any);
|
|
(model as any).isInteractive = () => false;
|
|
modelRef.current = model;
|
|
|
|
try { model.motion('Idle'); } catch { /* */ }
|
|
} catch (e) {
|
|
console.warn('[Live2DCat]', e);
|
|
}
|
|
};
|
|
|
|
init();
|
|
|
|
return () => {
|
|
mounted = false;
|
|
if (modelRef.current) {
|
|
app.stage.removeChild(modelRef.current);
|
|
modelRef.current.destroy?.();
|
|
modelRef.current = null;
|
|
}
|
|
};
|
|
}, [app]);
|
|
|
|
// Live2DCat no longer renders its own canvas — it shares the KiraAvatar canvas
|
|
return null;
|
|
}
|