import { useLayoutEffect, useRef, useState } from "react"; import { FESTIVAL_GREETINGS, getNewYearDisplayYear, type FestivalId } from "@/lib/festival"; import "./festival.css"; import "./newyear.css"; /** * VU 区的静态插画横幅:两端角色保持比例,中央背景平铺,文字独立缩放。 * 按容器宽度切换横排/双行,保留顶部设备信息;不参与布局、不拦截点击。 */ export default function FestivalGreeting({ festival, }: { festival: FestivalId; }) { const boxRef = useRef(null); const textBoxRef = useRef(null); const innerRef = useRef(null); const [scale, setScale] = useState(1); const [newYear] = useState(() => getNewYearDisplayYear()); const isNewYear = festival === "newyear"; const artworkRoot = `${import.meta.env.BASE_URL}images/festival/${festival}/`; useLayoutEffect(() => { const box = boxRef.current; const textBox = textBoxRef.current; const inner = innerRef.current; if (!box || !textBox || !inner) return; const status = box.parentElement?.querySelector( ".festival-vu-status" ); let disposed = false; const fit = () => { if (disposed) return; // 先应用排版再测量;使用容器宽度,兼容横屏、分屏及 legacy。 box.dataset.layout = box.clientWidth >= 420 ? "wide" : "compact"; // 极窄屏的格式和音量分成两行,标题跟随实际状态栏高度下移。 textBox.style.top = `${Math.max(17, status?.offsetHeight ?? 0) + 2}px`; const bw = textBox.clientWidth - 8; const bh = textBox.clientHeight - 4; const tw = inner.offsetWidth; const th = inner.offsetHeight; if (bw <= 0 || bh <= 0 || tw <= 0 || th <= 0) return; setScale(Math.min(bw / tw, bh / th)); }; fit(); const ro = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(fit); ro?.observe(box); ro?.observe(inner); if (status) ro?.observe(status); window.addEventListener("resize", fit); // 字体就绪后重新测量,避免初次回退字体导致文字越界。 void document.fonts?.ready.then(fit); return () => { disposed = true; ro?.disconnect(); window.removeEventListener("resize", fit); }; }, [festival]); return (