Initialisieren

This commit is contained in:
2026-07-23 21:59:15 +02:00
commit 2d961adad4
76 changed files with 14318 additions and 0 deletions

View File

@@ -0,0 +1,390 @@
"use client";
import { useEffect, useRef } from "react";
import { motion } from "framer-motion";
import { gsap } from "@/lib/gsap";
/**
* Botanisch präzise verschnörkelte Ranken:
* Das Wachstum ist exakt auf den gesamten Scrollverlauf abgestimmt (kein zu schnelles Wachsen).
* Die Ranken entfalten sich genau synchron zur Scroll-Position und erreichen den
* untersten Rand erst, wenn man auch ganz unten angekommen ist.
*/
// Botanisch geformtes Blatt mit spitzer Silhouetten-Form, Blattader & dezentem Hover-Wiegeln
function Leaf({
x,
y,
rotate = 0,
scale = 1,
className = "",
}: {
x: number;
y: number;
rotate?: number;
scale?: number;
className?: string;
}) {
return (
<motion.g
transform={`translate(${x}, ${y}) rotate(${rotate}) scale(${scale})`}
whileHover={{ scale: scale * 1.18, rotate: rotate + 6 }}
transition={{ type: "spring", stiffness: 160, damping: 16 }}
className={`vine-leaf pointer-events-auto cursor-pointer ${className}`}
>
{/* Äußere Blattsilhouette edles Gelb-Grün / Smaragd */}
<path
d="M 0 0 C 14 -20 36 -12 42 0 C 36 12 14 20 0 0 Z"
fill="#0d7a57"
fillOpacity="0.92"
className="transition-all duration-300 hover:fill-[#109b6f]"
/>
{/* Feine Blattader */}
<path
d="M 0 0 C 15 -3 28 -2 39 0"
stroke="#ffffff"
strokeOpacity="0.75"
strokeWidth="1.2"
strokeLinecap="round"
/>
</motion.g>
);
}
// Offene Spiral-Tendril (Ranken-Kringel)
function Tendril({ d, className = "" }: { d: string; className?: string }) {
return (
<path
className={`vine-tendril ${className}`}
d={d}
fill="none"
stroke="#161513"
strokeWidth="1.4"
strokeLinecap="round"
vectorEffect="non-scaling-stroke"
/>
);
}
// Ranke 1 (Links-Oben nach Mitte-Rechts - Hero Bereich)
const STEM_1 =
"M -40 20 C 120 70, 240 160, 320 300 C 400 440, 280 600, 420 740 C 520 860, 680 940, 820 1060";
const TENDRILS_1 = [
"M 240 160 C 275 135, 295 160, 275 185 C 255 195, 240 178, 252 165",
"M 420 740 C 465 710, 485 745, 460 770 C 435 785, 415 755, 428 738",
];
const LEAF_NODES_1 = [
{ x: 120, y: 70, rotate: -25, scale: 0.9 },
{ x: 240, y: 160, rotate: 42, scale: 1.1 },
{ x: 320, y: 300, rotate: -45, scale: 1.0 },
{ x: 400, y: 440, rotate: 32, scale: 0.95 },
{ x: 420, y: 740, rotate: -35, scale: 1.05 },
{ x: 680, y: 940, rotate: 50, scale: 0.9 },
];
// Ranke 2 (Rechts-Oben edel schwebend - Mittlerer Bereich)
const STEM_2 =
"M 920 30 C 760 150, 620 310, 710 490 C 800 680, 620 830, 740 1020 C 860 1180, 720 1340, 840 1500";
const TENDRILS_2 = [
"M 710 490 C 665 460, 640 495, 665 520 C 690 535, 710 505, 695 488",
"M 740 1020 C 695 990, 670 1025, 695 1050 C 720 1065, 740 1035, 725 1018",
];
const LEAF_NODES_2 = [
{ x: 760, y: 150, rotate: 155, scale: 1.0 },
{ x: 710, y: 490, rotate: -130, scale: 1.15 },
{ x: 620, y: 830, rotate: 140, scale: 0.9 },
{ x: 740, y: 1020, rotate: -150, scale: 1.05 },
{ x: 840, y: 1500, rotate: 135, scale: 1.1 },
];
// Ranke 3 (Untere Ranke Links Wächst erst in der 2. Seitenhälfte bis in den Footer)
const STEM_3 =
"M -30 50 C 210 180, 440 240, 520 430 C 600 620, 420 810, 610 1000 C 780 1170, 690 1340, 840 1550 C 920 1720, 780 1880, 910 2050";
const TENDRILS_3 = [
"M 520 430 C 565 400, 590 435, 565 460 C 540 475, 518 445, 532 428",
"M 610 1000 C 655 970, 680 1005, 655 1030 C 630 1045, 608 1015, 622 998",
"M 840 1550 C 885 1520, 910 1555, 885 1580 C 860 1595, 838 1565, 852 1548",
];
const LEAF_NODES_3 = [
{ x: 210, y: 180, rotate: -30, scale: 1.05 },
{ x: 440, y: 240, rotate: 35, scale: 0.9 },
{ x: 520, y: 430, rotate: -40, scale: 1.1 },
{ x: 420, y: 810, rotate: 45, scale: 1.0 },
{ x: 610, y: 1000, rotate: -35, scale: 1.15 },
{ x: 780, y: 1170, rotate: 30, scale: 0.95 },
{ x: 840, y: 1550, rotate: -25, scale: 1.1 },
{ x: 880, y: 1860, rotate: 40, scale: 0.95 },
];
// Ranke 4 (Untere Ranke Rechts Wächst erst in der 2. Seitenhälfte bis in den Footer)
const STEM_4 =
"M 900 40 C 740 220, 620 400, 700 600 C 780 800, 600 1000, 720 1220 C 840 1440, 680 1680, 780 1920 C 880 2080, 750 2200, 850 2320";
const TENDRILS_4 = [
"M 700 600 C 655 570, 630 605, 655 630 C 680 645, 700 615, 685 598",
"M 720 1220 C 675 1190, 650 1225, 675 1250 C 700 1265, 720 1235, 705 1218",
];
const LEAF_NODES_4 = [
{ x: 740, y: 220, rotate: 145, scale: 1.0 },
{ x: 700, y: 600, rotate: 125, scale: 0.95 },
{ x: 600, y: 1000, rotate: -140, scale: 1.1 },
{ x: 720, y: 1220, rotate: 135, scale: 1.05 },
{ x: 780, y: 1920, rotate: -130, scale: 0.95 },
];
export default function ScrollFlourishes() {
const rootRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const root = rootRef.current;
if (!root) return;
const mm = gsap.matchMedia();
mm.add("(prefers-reduced-motion: no-preference)", () => {
// 1. Dasharray & Offset für alle Linienpfade vorbereiten
const strokePaths = root.querySelectorAll<SVGPathElement>(
".vine-stem, .vine-tendril",
);
strokePaths.forEach((p) => {
const len = p.getTotalLength();
p.style.strokeDasharray = `${len}`;
p.style.strokeDashoffset = `${len}`;
});
const leaves = root.querySelectorAll<SVGGElement>(".vine-leaf");
gsap.set(leaves, { opacity: 0, scale: 0, transformOrigin: "0px 0px" });
// 2. Perfekt getaktete ScrollTrigger Timeline (Spannweite von 0 bis 1.0 des gesamten Dokument-Scrolls)
const tl = gsap.timeline({
scrollTrigger: {
trigger: document.body,
start: "top top",
end: "bottom bottom",
scrub: 0.7,
},
});
// Ranke 1 (Hero): Wächst im oberen Drittel (0.0 bis 0.38)
tl.to(
root.querySelectorAll(".vine-stem-1"),
{ strokeDashoffset: 0, ease: "none", duration: 0.38 },
0,
);
tl.to(
root.querySelectorAll(".vine-tendril-1"),
{ strokeDashoffset: 0, ease: "none", duration: 0.08 },
0.25,
);
// Ranke 2 (Mitte-Rechts): Wächst im mittleren Bereich (0.22 bis 0.68)
tl.to(
root.querySelectorAll(".vine-stem-2"),
{ strokeDashoffset: 0, ease: "none", duration: 0.46 },
0.22,
);
tl.to(
root.querySelectorAll(".vine-tendril-2"),
{ strokeDashoffset: 0, ease: "none", duration: 0.08 },
0.48,
);
// Ranke 3 (Untere Ranke Links): Wächst in der 2. Hälfte bis ganz nach unten (0.45 bis 1.0)
tl.to(
root.querySelectorAll(".vine-stem-3"),
{ strokeDashoffset: 0, ease: "none", duration: 0.55 },
0.45,
);
tl.to(
root.querySelectorAll(".vine-tendril-3"),
{ strokeDashoffset: 0, ease: "none", duration: 0.08 },
0.72,
);
// Ranke 4 (Untere Ranke Rechts): Wächst in der 2. Hälfte bis ganz nach unten (0.50 bis 1.0)
tl.to(
root.querySelectorAll(".vine-stem-4"),
{ strokeDashoffset: 0, ease: "none", duration: 0.5 },
0.5,
);
tl.to(
root.querySelectorAll(".vine-tendril-4"),
{ strokeDashoffset: 0, ease: "none", duration: 0.08 },
0.78,
);
// Blätter entfalten sich organisch zur jeweiligen Wachstumszeit
tl.to(
leaves,
{
opacity: 1,
scale: 1,
ease: "back.out(1.5)",
duration: 0.1,
stagger: 0.02,
},
0.1,
);
// Subtiler, ruhiger Parallax-Versatz (Ohne hektische Maus-Swayings)
tl.to(
".vine-container-1",
{ yPercent: 6, scale: 1.06, ease: "none", duration: 1 },
0,
);
tl.to(
".vine-container-2",
{ yPercent: 8, scale: 0.95, ease: "none", duration: 1 },
0,
);
tl.to(
".vine-container-3",
{ yPercent: -4, scale: 1.05, ease: "none", duration: 1 },
0,
);
tl.to(
".vine-container-4",
{ yPercent: 5, scale: 1.04, ease: "none", duration: 1 },
0,
);
});
return () => mm.revert();
}, []);
return (
<div
ref={rootRef}
aria-hidden
className="pointer-events-none fixed inset-0 -z-10 overflow-hidden select-none"
>
{/* Ranke 1: Links-Oben nach Mitte-Rechts (Hero) */}
<div className="vine-container-1 absolute left-[-4vw] top-[-2vh] w-[50vw] max-w-[750px] min-w-[320px] opacity-35 transition-opacity duration-500">
<svg
viewBox="0 0 850 1100"
fill="none"
strokeLinecap="round"
className="w-full h-auto pointer-events-auto"
>
<path
className="vine-stem vine-stem-1"
d={STEM_1}
stroke="#161513"
strokeWidth="1.6"
vectorEffect="non-scaling-stroke"
/>
{TENDRILS_1.map((d, i) => (
<Tendril key={i} className="vine-tendril-1" d={d} />
))}
{LEAF_NODES_1.map((node, i) => (
<Leaf
key={i}
x={node.x}
y={node.y}
rotate={node.rotate}
scale={node.scale}
/>
))}
</svg>
</div>
{/* Ranke 2: Rechts-Oben (Mittlerer Bereich) */}
<div className="vine-container-2 absolute right-[-4vw] top-[18vh] w-[54vw] max-w-[820px] min-w-[340px] opacity-30 transition-opacity duration-500">
<svg
viewBox="0 0 950 1550"
fill="none"
strokeLinecap="round"
className="w-full h-auto pointer-events-auto"
>
<path
className="vine-stem vine-stem-2"
d={STEM_2}
stroke="#161513"
strokeWidth="1.6"
vectorEffect="non-scaling-stroke"
/>
{TENDRILS_2.map((d, i) => (
<Tendril key={i} className="vine-tendril-2" d={d} />
))}
{LEAF_NODES_2.map((node, i) => (
<Leaf
key={i}
x={node.x}
y={node.y}
rotate={node.rotate}
scale={node.scale}
/>
))}
</svg>
</div>
{/* Ranke 3: Untere Ranke Links Wächst erst in der 2. Seitenhälfte bis in den Footer */}
<div className="vine-container-3 absolute left-[-3vw] top-[42vh] w-[58vw] max-w-[920px] min-w-[360px] opacity-35 transition-opacity duration-500">
<svg
viewBox="0 0 950 2100"
fill="none"
strokeLinecap="round"
className="w-full h-auto pointer-events-auto"
>
<path
className="vine-stem vine-stem-3"
d={STEM_3}
stroke="#161513"
strokeWidth="1.6"
vectorEffect="non-scaling-stroke"
/>
{TENDRILS_3.map((d, i) => (
<Tendril key={i} className="vine-tendril-3" d={d} />
))}
{LEAF_NODES_3.map((node, i) => (
<Leaf
key={i}
x={node.x}
y={node.y}
rotate={node.rotate}
scale={node.scale}
/>
))}
</svg>
</div>
{/* Ranke 4: Untere Ranke Rechts Wächst erst in der 2. Seitenhälfte bis in den Footer */}
<div className="vine-container-4 absolute right-[-3vw] top-[48vh] w-[56vw] max-w-[900px] min-w-[350px] opacity-35 transition-opacity duration-500">
<svg
viewBox="0 0 950 2350"
fill="none"
strokeLinecap="round"
className="w-full h-auto pointer-events-auto"
>
<path
className="vine-stem vine-stem-4"
d={STEM_4}
stroke="#161513"
strokeWidth="1.6"
vectorEffect="non-scaling-stroke"
/>
{TENDRILS_4.map((d, i) => (
<Tendril key={i} className="vine-tendril-4" d={d} />
))}
{LEAF_NODES_4.map((node, i) => (
<Leaf
key={i}
x={node.x}
y={node.y}
rotate={node.rotate}
scale={node.scale}
/>
))}
</svg>
</div>
</div>
);
}