// hero.jsx — Hero + анимированная инфографика круговорота const { useEffect, useRef, useState } = React; // useInView — простой хук, чтобы стартовать анимацию при появлении function useInView(threshold = 0.15) { const ref = useRef(null); const [seen, setSeen] = useState(false); useEffect(() => { if (!ref.current || seen) return; // First, check if element is already in view on mount (e.g. hero above the fold) const rect = ref.current.getBoundingClientRect(); const vh = window.innerHeight || 800; if (rect.top < vh && rect.bottom > 0) { setSeen(true); return; } const io = new IntersectionObserver(([e]) => { if (e.isIntersecting) {setSeen(true);io.disconnect();} }, { threshold }); io.observe(ref.current); return () => io.disconnect(); }, [seen, threshold]); return [ref, seen]; } window.useInView = useInView; function CycleDiagram() { // Анимированный круговорот: 4 узла на окружности, вращается медленно const nodes = [ { label: "Очистки", sub: "0,5–1 кг / сутки", angle: -90, color: "var(--accent)" }, { label: "Черви", sub: "Eisenia fetida", angle: 0, color: "var(--accent-2)" }, { label: "Биогумус", sub: "Премиум-удобрение", angle: 90, color: "var(--accent)" }, { label: "Урожай", sub: "+25–40% к норме", angle: 180, color: "var(--accent-2)" }]; return (
{/* HTML-узлы поверх SVG — для нормальной типографики */} {nodes.map((n, i) => { const rad = n.angle * Math.PI / 180; const x = 50 + 44 * Math.cos(rad); const y = 50 + 44 * Math.sin(rad); return (
{n.label}
{n.sub}
); })}
); } window.CycleDiagram = CycleDiagram; function StatCounter({ to, suffix = "", duration = 1400, decimals = 0, delay = 0 }) { const [val, setVal] = useState(0); const ref = useRef(null); const [started, setStarted] = useState(false); useEffect(() => { if (started || !ref.current) return; const el = ref.current; const start = () => { if (started) return; setStarted(true); }; const rect = el.getBoundingClientRect(); const vh = window.innerHeight || 800; // Already visible? Start now. if (rect.top < vh + 200 && rect.bottom > -200) { const t = setTimeout(start, delay); return () => clearTimeout(t); } const io = new IntersectionObserver(([e]) => { if (e.isIntersecting) {start();io.disconnect();} }, { threshold: 0.1 }); io.observe(el); return () => io.disconnect(); }, [started, delay]); useEffect(() => { if (!started) return; const t0 = performance.now(); let raf; const step = (t) => { const p = Math.min(1, (t - t0) / duration); const eased = 1 - Math.pow(1 - p, 3); setVal(to * eased); if (p < 1) raf = requestAnimationFrame(step); }; raf = requestAnimationFrame(step); return () => cancelAnimationFrame(raf); }, [started, to, duration]); const formatted = decimals ? val.toFixed(decimals) : Math.round(val).toLocaleString("ru-RU"); return {formatted}{suffix}; } window.StatCounter = StatCounter; function Hero({ heroTitle, heroSub, logoVariant }) { const [ref, seen] = useInView(0); return (
домашняя вермиферма · ekokrug.ru

{heroTitle}

{heroSub}

меньше мусора от семьи
×
эффективнее навоза
комплект с поддержкой
ЭкоКруг — 4-ярусная вермиферма в кухне
Очистки
Черви работают
Биогумус
Вермичай
{/* Декоративные фоновые элементы */}
); } window.Hero = Hero;