// portal-sections.jsx — Nav, Hero, Districts, Zoo, Footer function Nav() { const [scrolled, setScrolled] = React.useState(false); React.useEffect(() => { const onScroll = () => setScrolled(window.scrollY > 60); window.addEventListener("scroll", onScroll); return () => window.removeEventListener("scroll", onScroll); }, []); return ( ); } function HeroSlideshow() { const [images, setImages] = React.useState(null); // null=読込中, []=画像なし const [idx, setIdx] = React.useState(0); const timer = React.useRef(null); const INTERVAL = 5500; // images/ フォルダの一覧を取得(フォルダの増減に自動対応) React.useEffect(() => { let alive = true; fetch("images_api.php", { credentials: "same-origin" }) .then(r => r.ok ? r.json() : Promise.reject()) .then(d => { if (alive) setImages(Array.isArray(d.images) ? d.images : []); }) .catch(() => { if (alive) setImages([]); }); return () => { alive = false; }; }, []); const count = images ? images.length : 0; // 自動再生 React.useEffect(() => { if (count <= 1) return; timer.current = setInterval(() => setIdx(i => (i + 1) % count), INTERVAL); return () => clearInterval(timer.current); }, [count]); const go = (n) => { setIdx((n + count) % count); if (timer.current) { clearInterval(timer.current); } if (count > 1) timer.current = setInterval(() => setIdx(i => (i + 1) % count), INTERVAL); }; // 画像が無い / 読込中は、従来のイラスト背景を表示 if (!images || count === 0) { return
; } return (
{images.map((src, i) => (
))} {count > 1 && ( <>
{images.map((_, i) => (
)}
); } function Hero() { return (


EST. 1955 ─ HIROSHIMA
広島市 安佐北区 ─ HIROSHIMA CITY · ASAKITA-KU

ふるさとは、
少し遠く。

広島の北、太田川と十の地区が、川と山と人でつながる町。
季節とともに、ゆっくり生きるための場所があります。

{DISTRICTS.map((d, i) => (
{d.name} {d.ro}
{i < DISTRICTS.length - 1 && ·}
))} + 安佐動物公園
SCROLL ─ お知らせへ
34°35′N · 132°27′E · 125.1 KM²
); } function DistrictsStrip() { return (
02 ─ TEN DISTRICTS

十の地区Ten Districts, One Town

飯室・鈴張・久地・久地南・小河内・毛木・後山・宮野・筒瀬・あさひが丘。それぞれの風土を育んできた十の地区が、太田川と里山に抱かれて連なります。
{DISTRICTS.map(d => (
{d.idx}
{d.name}
{d.ro}
{d.pop && d.pop !== "—" && (
人口{d.pop}
)}
特徴{d.feat}
))}
); } function ZooSlideshow() { const [images, setImages] = React.useState(null); // null=読込中, []=画像なし const [idx, setIdx] = React.useState(0); const timer = React.useRef(null); const INTERVAL = 5000; // asazoo/ フォルダの一覧を取得(フォルダの増減に自動対応) React.useEffect(() => { let alive = true; fetch("asazoo_api.php", { credentials: "same-origin" }) .then(r => r.ok ? r.json() : Promise.reject()) .then(d => { if (alive) setImages(Array.isArray(d.images) ? d.images : []); }) .catch(() => { if (alive) setImages([]); }); return () => { alive = false; }; }, []); const count = images ? images.length : 0; React.useEffect(() => { if (count <= 1) return; timer.current = setInterval(() => setIdx(i => (i + 1) % count), INTERVAL); return () => clearInterval(timer.current); }, [count]); const go = (n) => { setIdx((n + count) % count); if (timer.current) clearInterval(timer.current); if (count > 1) timer.current = setInterval(() => setIdx(i => (i + 1) % count), INTERVAL); }; // 画像が無い / 読込中は、従来のプレースホルダ(獸+足跡)を表示 if (!images || count === 0) { return ( <>
{[{x:60,y:90},{x:140,y:330},{x:320,y:150},{x:340,y:300},{x:80,y:230}].map((p,i)=>( ))} ); } return (
{images.map((src, i) => (
))} {count > 1 && ( <>
{images.map((_, i) => (
)}
); } function ZooSection() { return (
PHOTO ─ 安佐動物公園
03 ─ ICONIC SPOT

安佐動物公園Asa Zoological Park · since 1971

日浦の丘陵地に広がる、広島市民の動物園。アフリカからやってきたキリンやサイ、希少なツキノワグマまで、約160種・1700頭羽が暮らしています。安佐町の里山に開かれた、季節を映す動物園。

開園
1971
動物
160
面積
25.6ha
入園
510
); } function Footer({ toast }) { return ( ); } Object.assign(window, { Nav, Hero, DistrictsStrip, ZooSection, Footer });