fix(pets): dynamically position cat at PetZone DOM element
Cat position is now measured from the [data-petzone] DOM element's getBoundingClientRect(), so it always aligns with the PetZone section regardless of window size or sidebar content height. Removed Live2DCat import from PetZone (cat renders on shared stage).
This commit is contained in:
@@ -12,49 +12,38 @@ interface Props {
|
||||
* - Mochi the cat (PetZone, bottom of right sidebar)
|
||||
*
|
||||
* Canvas sits behind UI panels (z-0, pointer-events: none).
|
||||
* Cat position is dynamically measured from the [data-petzone] DOM element.
|
||||
*/
|
||||
|
||||
// --- Layout constants (must match Tailwind in App.tsx) ---
|
||||
// Left sidebar: w-72 = 288px, gap-4 = 16px, px-4 = 16px
|
||||
// Right sidebar: w-64 = 256px, bottom bar: ~40px
|
||||
const PAD = 16;
|
||||
const LEFT_W = 288;
|
||||
const GAP = 16;
|
||||
const RIGHT_W = 256;
|
||||
const BOTTOM_BAR_H = 40;
|
||||
|
||||
function positionModels(
|
||||
kira: any, cat: any,
|
||||
appW: number, appH: number,
|
||||
) {
|
||||
// Center panel center
|
||||
function positionKira(kira: any, appW: number, appH: number) {
|
||||
const centerLeft = PAD + LEFT_W + GAP;
|
||||
const centerRight = appW - GAP - RIGHT_W - PAD;
|
||||
const centerMidX = (centerLeft + centerRight) / 2;
|
||||
const centerMidY = appH * 0.46;
|
||||
|
||||
if (kira) {
|
||||
const s = Math.min(((centerRight - centerLeft) * 0.78) / kira.width, (appH * 0.78) / kira.height);
|
||||
kira.scale.set(s);
|
||||
kira.anchor.set(0.5, 0.5);
|
||||
kira.position.set(centerMidX, centerMidY);
|
||||
}
|
||||
|
||||
// Right sidebar center
|
||||
const rightLeft = appW - RIGHT_W - PAD;
|
||||
const rightMidX = rightLeft + RIGHT_W / 2;
|
||||
const catY = appH - BOTTOM_BAR_H - 50;
|
||||
function positionCat(cat: any) {
|
||||
const el = document.querySelector('[data-petzone]');
|
||||
if (!el || !cat) return;
|
||||
const r = el.getBoundingClientRect();
|
||||
|
||||
if (cat) {
|
||||
// Set scale to 1 first to get natural bounds, then compute target
|
||||
cat.scale.set(1);
|
||||
const naturalH = cat.getLocalBounds().height;
|
||||
const targetPx = 100; // ~100px tall rendered
|
||||
const targetPx = 100;
|
||||
const cs = naturalH > 0 ? targetPx / naturalH : 0.05;
|
||||
cat.scale.set(cs);
|
||||
cat.anchor.set(0.5, 0.5);
|
||||
cat.position.set(rightMidX, catY);
|
||||
}
|
||||
cat.position.set(r.left + r.width / 2, r.top + r.height / 2 + 10);
|
||||
}
|
||||
|
||||
export default function Live2DStage({ onKiraReady, onReady }: Props) {
|
||||
@@ -92,7 +81,8 @@ export default function Live2DStage({ onKiraReady, onReady }: Props) {
|
||||
|
||||
const onResize = () => {
|
||||
app.renderer.resize(window.innerWidth, window.innerHeight);
|
||||
positionModels(kiraModel, catModel, app.screen.width, app.screen.height);
|
||||
if (kiraModel) positionKira(kiraModel, app.screen.width, app.screen.height);
|
||||
if (catModel) positionCat(catModel);
|
||||
};
|
||||
window.addEventListener('resize', onResize);
|
||||
|
||||
@@ -102,7 +92,7 @@ export default function Live2DStage({ onKiraReady, onReady }: Props) {
|
||||
});
|
||||
if (!mounted) { app.destroy(true); return; }
|
||||
|
||||
positionModels(kiraModel, null, app.screen.width, app.screen.height);
|
||||
positionKira(kiraModel, app.screen.width, app.screen.height);
|
||||
(kiraModel as any).isInteractive = () => false;
|
||||
app.stage.addChild(kiraModel as any);
|
||||
|
||||
@@ -118,7 +108,9 @@ export default function Live2DStage({ onKiraReady, onReady }: Props) {
|
||||
});
|
||||
if (!mounted) return;
|
||||
|
||||
positionModels(kiraModel, catModel, app.screen.width, app.screen.height);
|
||||
// Small delay to ensure PetZone DOM is rendered
|
||||
await new Promise(r => setTimeout(r, 100));
|
||||
positionCat(catModel);
|
||||
(catModel as any).isInteractive = () => false;
|
||||
app.stage.addChild(catModel as any);
|
||||
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import Live2DCat from './Live2DCat';
|
||||
|
||||
export default function PetZone() {
|
||||
return (
|
||||
<div className="p-4 relative overflow-hidden" style={{ minHeight: 160 }}>
|
||||
<div data-petzone className="p-4 relative overflow-hidden" style={{ minHeight: 140 }}>
|
||||
<h3 className="text-sm font-bold text-kira-plum mb-2 flex items-center gap-2">
|
||||
<span>🐱</span> Pet Zone
|
||||
</h3>
|
||||
<div className="flex flex-col items-center">
|
||||
<Live2DCat className="w-28 h-28" />
|
||||
<span className="text-[10px] text-kira-plum/60 mt-1 font-medium">Mochi</span>
|
||||
<div className="flex flex-col items-center pt-2">
|
||||
<span className="text-[10px] text-kira-plum/60 font-medium">Mochi</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user