88 lines
3.1 KiB
TypeScript
88 lines
3.1 KiB
TypeScript
![]() |
"use client";
|
||
|
|
||
|
import { useEffect, useRef } from "react";
|
||
|
|
||
|
export function HeroDataStreamEffect() {
|
||
|
const svgRef = useRef<SVGSVGElement>(null);
|
||
|
|
||
|
useEffect(() => {
|
||
|
const svg = svgRef.current;
|
||
|
if (!svg) return;
|
||
|
|
||
|
const resizeSVG = () => {
|
||
|
const headerElement = document.querySelector(".hero-header");
|
||
|
if (headerElement) {
|
||
|
const rect = headerElement.getBoundingClientRect();
|
||
|
const width = Math.max(rect.width + 200, window.innerWidth);
|
||
|
const height = Math.max(rect.height + 200, window.innerHeight);
|
||
|
svg.setAttribute("width", `${width}px`);
|
||
|
svg.setAttribute("height", `${height}px`);
|
||
|
svg.style.top = `${Math.min(rect.top - 100, 0)}px`;
|
||
|
svg.style.left = `${(window.innerWidth - width) / 2}px`;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
resizeSVG();
|
||
|
window.addEventListener("resize", resizeSVG);
|
||
|
|
||
|
const createStream = () => {
|
||
|
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
||
|
const startX = Math.random() * svg.clientWidth;
|
||
|
const startY = -50;
|
||
|
const endX = Math.random() * svg.clientWidth;
|
||
|
const endY = svg.clientHeight + 50;
|
||
|
const midX1 = startX + (Math.random() - 0.5) * 200;
|
||
|
const midY1 = startY + (endY - startY) * 0.33;
|
||
|
const midX2 = endX + (Math.random() - 0.5) * 200;
|
||
|
const midY2 = startY + (endY - startY) * 0.66;
|
||
|
|
||
|
const d = `M ${startX} ${startY} C ${midX1} ${midY1}, ${midX2} ${midY2}, ${endX} ${endY}`;
|
||
|
path.setAttribute("d", d);
|
||
|
path.setAttribute("stroke", "rgba(94, 81, 236, 0.4)");
|
||
|
path.setAttribute("stroke-width", "1.5");
|
||
|
path.setAttribute("fill", "none");
|
||
|
path.setAttribute("opacity", "0");
|
||
|
|
||
|
const length = path.getTotalLength();
|
||
|
path.setAttribute("stroke-dasharray", length.toString());
|
||
|
path.setAttribute("stroke-dashoffset", length.toString());
|
||
|
|
||
|
svg.appendChild(path);
|
||
|
|
||
|
return { path, length };
|
||
|
};
|
||
|
|
||
|
const streams: { path: SVGPathElement; length: number }[] = [];
|
||
|
for (let i = 0; i < 15; i++) {
|
||
|
streams.push(createStream());
|
||
|
}
|
||
|
|
||
|
let progress = 0;
|
||
|
const animate = () => {
|
||
|
progress += 0.003;
|
||
|
streams.forEach(({ path, length }, index) => {
|
||
|
const offset = (progress + index * 0.1) % 1;
|
||
|
const opacity = Math.sin(offset * Math.PI) * 0.7;
|
||
|
path.setAttribute("stroke-dashoffset", ((1 - offset) * length).toString());
|
||
|
path.setAttribute("opacity", opacity.toString());
|
||
|
});
|
||
|
|
||
|
requestAnimationFrame(animate);
|
||
|
};
|
||
|
|
||
|
animate();
|
||
|
|
||
|
return () => {
|
||
|
window.removeEventListener("resize", resizeSVG);
|
||
|
};
|
||
|
}, []);
|
||
|
|
||
|
return (
|
||
|
<svg
|
||
|
ref={svgRef}
|
||
|
className="absolute pointer-events-none -z-50 scale-y-150"
|
||
|
style={{ filter: "drop-shadow(0 0 10px rgba(94, 81, 236, 0.3))" }}
|
||
|
/>
|
||
|
);
|
||
|
}
|