Files
kira/frontend/src/components/Live2DCat.tsx
T
hobokenchicken be1e51cc9a fix(webgl): share single Pixi context between KiraAvatar and Live2DCat
Eliminates WebGL bindBuffer/bindTexture spam from dual Application contexts.
Cat model now loads onto KiraAvatar's shared stage via onAppReady callback.
2026-06-05 13:04:43 -04:00

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;
}