You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
126 lines
4.2 KiB
126 lines
4.2 KiB
|
1 month ago
|
"use client";
|
||
|
|
|
||
|
|
import React, { useEffect, useRef } from "react";
|
||
|
|
import type { HeroData } from "../types";
|
||
|
|
|
||
|
|
interface HeroProps {
|
||
|
|
data: HeroData;
|
||
|
|
locale?: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function Hero({ data, locale = "zh-CN" }: HeroProps) {
|
||
|
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const canvas = canvasRef.current;
|
||
|
|
if (!canvas) return;
|
||
|
|
|
||
|
|
const ctx = canvas.getContext("2d");
|
||
|
|
if (!ctx) return;
|
||
|
|
|
||
|
|
function resize() {
|
||
|
|
if (!canvas) return;
|
||
|
|
canvas.width = canvas.clientWidth;
|
||
|
|
canvas.height = canvas.clientHeight;
|
||
|
|
}
|
||
|
|
|
||
|
|
window.addEventListener("resize", resize);
|
||
|
|
resize();
|
||
|
|
|
||
|
|
const particles: Array<{
|
||
|
|
x: number;
|
||
|
|
y: number;
|
||
|
|
r: number;
|
||
|
|
vx: number;
|
||
|
|
vy: number;
|
||
|
|
alpha: number;
|
||
|
|
}> = [];
|
||
|
|
|
||
|
|
if (canvas) {
|
||
|
|
for (let i = 0; i < 60; i++) {
|
||
|
|
particles.push({
|
||
|
|
x: Math.random() * canvas.width,
|
||
|
|
y: Math.random() * canvas.height,
|
||
|
|
r: Math.random() * 1.8 + 0.6,
|
||
|
|
vx: (Math.random() - 0.5) * 0.2,
|
||
|
|
vy: (Math.random() - 0.5) * 0.2,
|
||
|
|
alpha: Math.random() * 0.6 + 0.2,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function draw() {
|
||
|
|
if (!ctx || !canvas) return;
|
||
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||
|
|
for (const p of particles) {
|
||
|
|
p.x += p.vx;
|
||
|
|
p.y += p.vy;
|
||
|
|
if (p.x < -10) p.x = canvas.width + 10;
|
||
|
|
if (p.x > canvas.width + 10) p.x = -10;
|
||
|
|
if (p.y < -10) p.y = canvas.height + 10;
|
||
|
|
if (p.y > canvas.height + 10) p.y = -10;
|
||
|
|
ctx.beginPath();
|
||
|
|
ctx.fillStyle = `rgba(196,161,75,${p.alpha})`;
|
||
|
|
ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
|
||
|
|
ctx.fill();
|
||
|
|
}
|
||
|
|
requestAnimationFrame(draw);
|
||
|
|
}
|
||
|
|
draw();
|
||
|
|
|
||
|
|
return () => {
|
||
|
|
window.removeEventListener("resize", resize);
|
||
|
|
};
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
|
||
|
|
return (
|
||
|
|
<section className="relative py-12 md:py-20 pb-6 md:pb-10 bg-gradient-to-b from-[rgba(8,8,8,0.6)] to-[rgba(11,11,11,1)] overflow-hidden">
|
||
|
|
<div className="max-w-[1200px] mx-auto px-4 md:px-6 flex items-center gap-6 md:gap-10 flex-col lg:flex-row">
|
||
|
|
<div className="flex-1 max-w-[640px] w-full">
|
||
|
|
<h1 className="text-2xl sm:text-3xl md:text-[40px] mb-3 leading-[1.05] text-white font-bold">
|
||
|
|
{data.title}
|
||
|
|
</h1>
|
||
|
|
<p className="text-sm sm:text-base text-huilong-muted mb-4 md:mb-6 leading-relaxed">{data.subtitle}</p>
|
||
|
|
<div className="flex flex-wrap gap-2 md:gap-3">
|
||
|
|
<a
|
||
|
|
href={`/${locale}/solutions`}
|
||
|
|
className="inline-block px-4 md:px-[18px] py-2 md:py-3 text-sm md:text-base rounded-lg bg-huilong-gold text-huilong-bg font-semibold no-underline shadow-[0_6px_18px_rgba(196,161,75,0.12)] hover:opacity-90 active:opacity-80 transition-opacity"
|
||
|
|
>
|
||
|
|
{data.cta1}
|
||
|
|
</a>
|
||
|
|
<a
|
||
|
|
href={data.cta2Href}
|
||
|
|
className="inline-block px-4 md:px-[18px] py-2 md:py-3 text-sm md:text-base rounded-lg border border-[rgba(196,161,75,0.18)] text-huilong-gold bg-transparent font-semibold no-underline hover:opacity-80 active:opacity-70 transition-opacity"
|
||
|
|
>
|
||
|
|
{data.cta2}
|
||
|
|
</a>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div className="flex-1 flex justify-center items-center relative h-64 sm:h-72 md:h-80 w-full lg:w-auto">
|
||
|
|
<div className="relative w-64 h-64 sm:w-72 sm:h-72 md:w-80 md:h-80">
|
||
|
|
<div className="absolute inset-0 rounded-full bg-[radial-gradient(circle_at_30%_30%,rgba(196,161,75,0.12),rgba(0,0,0,0.6))] flex items-center justify-center shadow-[0_8px_40px_rgba(0,0,0,0.6)]">
|
||
|
|
<div className="w-[140px] h-[140px] rounded-full bg-gradient-to-b from-[rgba(255,255,255,0.02)] to-[rgba(255,255,255,0.01)] border-2 border-[rgba(196,161,75,0.12)] backdrop-blur-sm" />
|
||
|
|
</div>
|
||
|
|
<canvas
|
||
|
|
ref={canvasRef}
|
||
|
|
className="absolute inset-0 w-full h-full pointer-events-none"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<svg
|
||
|
|
className="absolute bottom-0 left-0 w-full h-[100px]"
|
||
|
|
viewBox="0 0 1440 100"
|
||
|
|
preserveAspectRatio="none"
|
||
|
|
>
|
||
|
|
<path
|
||
|
|
d="M0,100 C360,0 1080,0 1440,100 L1440 100 L0 100 Z"
|
||
|
|
fill="#0b0b0b"
|
||
|
|
/>
|
||
|
|
</svg>
|
||
|
|
</section>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|