/* Animierter Ablauf ("So funktioniert's") — gemeinsam genutzt von index.html und so-funktionierts.html */ const { Icon } = window.WeiterfunkelnChDesignSystem_572ff0; function StarsIcon({ size = 24, main, accent, style }) { return ( ); } function AblaufNum({ children, delay, go }) { const d = go ? delay : 0; const dur = go ? 750 : 500; return (
{children}
); } function AblaufLine({ delay, go }) { const d = go ? delay : 0; const dur = go ? 550 : 450; return (
); } function AblaufConnectorCheck({ dCm, go }) { const d = go ? dCm : 0; const dur = go ? 600 : 500; return (
); } function AblaufConnectorTail({ d, go }) { return (
); } function AblaufTimeline({ loop = false, showLabels = true, onDone }) { const wrapRef = React.useRef(null); const sparkStageRef = React.useRef(null); const [go, setGo] = React.useState(false); const narrow = window.innerWidth < 720; const steps = [ { title: t("ablauf.titel1", "Gefunden"), text: t("ablauf.text1", "Durchsuche die verfügbaren Einzelstücke.") }, { title: t("ablauf.titel2", "Reserviert"), text: t("ablauf.text2", "Reserviere das Schmuckstück unverbindlich über die Website.") }, { title: t("ablauf.titel3", "Kontakt"), text: t("ablauf.text3", "Wir melden uns per E-Mail bei dir und klären Bezahlung, Versand oder Übergabe persönlich.") }, { title: t("ablauf.titel4", "Weiterfunkeln"), text: t("ablauf.text4", "Das Stück gehört dir — trag es mit Freude weiter.") } ]; // Zeitplan (ms): Zahl 1 → Häkchen 1 → Zahl 2 → Häkchen 2 → Zahl 3 → Häkchen 3 → Zahl 4 → Funkeln const T = { n1: 0, cm1: 350, n2: 1250, cm2: 2100, n3: 3050, cm3: 3900, n4: 4850, tail: 5050, finale: 5400 }; React.useEffect(() => { // Ein einziger, sich selbst fortsetzender Timer (Dependency-Array [] — läuft // unabhängig von `go` weiter). Wichtig: würde die Schleife stattdessen in einem // an [go] gebundenen Effect stecken, würde dessen eigener Cleanup beim // Zurücksetzen (go:true→false) sofort den gerade gesetzten Neustart-Timer // wieder löschen — die Animation liefe dann nur einmal. let cycleTimer; let alive = true; function showOnce() { // Doppeltes rAF: sorgt dafür, dass der Browser den Ausgangszustand (go=false) // sicher rendert, bevor auf go=true umgeschaltet wird — sonst überspringen // manche CSS-Transitions den Übergang und springen direkt ans Ziel. requestAnimationFrame(() => requestAnimationFrame(() => { if (alive) setGo(true); })); if (!loop) return; // einmaliger Durchlauf, bleibt danach im fertigen Zustand stehen cycleTimer = setTimeout(() => { if (!alive) return; setGo(false); cycleTimer = setTimeout(showOnce, 5000); }, T.finale + 3200); } const el = wrapRef.current; if (!el || !window.IntersectionObserver) { showOnce(); return () => { alive = false; clearTimeout(cycleTimer); }; } const obs = new IntersectionObserver((entries) => { if (entries[0].isIntersecting) { showOnce(); obs.disconnect(); } }, { threshold: 0.4 }); obs.observe(el); return () => { alive = false; obs.disconnect(); clearTimeout(cycleTimer); }; }, [loop]); React.useEffect(() => { if (!go || !onDone) return; const t = setTimeout(onDone, T.finale + 1500); return () => clearTimeout(t); }, [go]); React.useEffect(() => { if (!go || !sparkStageRef.current) return; const stage = sparkStageRef.current; let alive = true; let t; function spawn() { if (!alive) return; const s = document.createElement("span"); s.className = "wf-spark"; s.textContent = "✦"; s.style.left = (10 + Math.random() * 80) + "%"; s.style.top = (10 + Math.random() * 70) + "%"; s.style.fontSize = (26 + Math.random() * 20) + "px"; s.style.animationDuration = (1.3 + Math.random() * 1) + "s"; stage.appendChild(s); s.addEventListener("animationend", () => s.remove()); t = setTimeout(spawn, 400 + Math.random() * 550); } const start = setTimeout(spawn, T.finale + 200); return () => { alive = false; clearTimeout(start); clearTimeout(t); }; }, [go]); if (narrow) { return (
{steps.map((s, i) => { const delay = [T.n1, T.n2, T.n3, T.n4][i]; const d = go ? delay : 0; const dur = go ? 650 : 400; const fade = { opacity: go ? 1 : 0, transform: go ? "translateY(0)" : "translateY(6px)", transition: "opacity " + dur + "ms var(--ease-soft) " + d + "ms, transform " + dur + "ms var(--ease-soft) " + d + "ms" }; return (
{i + 1}

{s.title}

{showLabels &&

{s.text}

}
); })}
); } const fadeFor = delay => { const d = go ? delay : 0; const dur = go ? 650 : 400; return { opacity: go ? 1 : 0, transform: go ? "translateY(0)" : "translateY(6px)", transition: "opacity " + dur + "ms var(--ease-soft) " + d + "ms, transform " + dur + "ms var(--ease-soft) " + d + "ms" }; }; return (
1
2
3
4

{steps[0].title}

{showLabels &&

{steps[0].text}

}

{steps[1].title}

{showLabels &&

{steps[1].text}

}

{steps[2].title}

{showLabels &&

{steps[2].text}

}

{steps[3].title}

{showLabels &&

{steps[3].text}

}
); } Object.assign(window, { StarsIcon, AblaufTimeline });