Files
assetx/landingpage/components/SecuritySection.tsx
default 2ee4553b71 init: 初始化 AssetX 项目仓库
包含 webapp(Next.js 用户端)、webapp-back(Go 后端)、
antdesign(管理后台)、landingpage(营销落地页)、
数据库 SQL 和配置文件。
2026-03-27 11:26:43 +00:00

247 lines
9.5 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import { useState, useEffect, useRef } from 'react';
import Image from 'next/image';
import { Card, CardBody } from '@heroui/react';
import { SearchCheck, Landmark, BarChart2, ArrowRight, LucideIcon } from 'lucide-react';
import { useLanguage, TranslationKey } from '@/contexts/LanguageContext';
const features: { Icon: LucideIcon; titleKey: TranslationKey; descKey: TranslationKey | null; position: string; special?: boolean }[] = [
{
Icon: SearchCheck,
titleKey: 'security.audited.title',
descKey: 'security.audited.desc',
position: 'top-left'
},
{
Icon: Landmark,
titleKey: 'security.segregated.title',
descKey: 'security.segregated.desc',
position: 'top-right'
},
{
Icon: BarChart2,
titleKey: 'security.transparency.title',
descKey: 'security.transparency.desc',
position: 'bottom-left'
},
{
Icon: ArrowRight,
titleKey: 'security.partners.title',
descKey: null,
position: 'bottom-right',
special: true
}
];
export default function SecuritySection() {
const { t } = useLanguage();
const [animate, setAnimate] = useState(false);
const sectionRef = useRef<HTMLElement>(null);
const [cardHovers, setCardHovers] = useState<{[key: number]: {x: number, y: number} | null}>({});
useEffect(() => {
const currentRef = sectionRef.current;
if (!currentRef) return;
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting) {
setAnimate(true);
observer.disconnect();
}
},
{ threshold: 0.3 }
);
observer.observe(currentRef);
return () => observer.disconnect();
}, []);
const handleCardMouseMove = (e: React.MouseEvent<HTMLDivElement>, index: number) => {
const rect = e.currentTarget.getBoundingClientRect();
const x = ((e.clientX - rect.left) / rect.width) * 100;
const y = ((e.clientY - rect.top) / rect.height) * 100;
setCardHovers(prev => ({...prev, [index]: {x, y}}));
};
const handleCardMouseLeave = (index: number) => {
setCardHovers(prev => ({...prev, [index]: null}));
};
const getBorderGradients = (index: number) => {
const hover = cardHovers[index];
if (!hover) return null;
const { x, y } = hover;
const spreadRange = 20;
const horizontalGradient = `linear-gradient(to right,
transparent 0%,
rgba(136,136,136,0.3) ${Math.max(0, x - spreadRange)}%,
rgba(136,136,136,1) ${x}%,
rgba(136,136,136,0.3) ${Math.min(100, x + spreadRange)}%,
transparent 100%)`;
const verticalGradient = `linear-gradient(to bottom,
transparent 0%,
rgba(136,136,136,0.3) ${Math.max(0, y - spreadRange)}%,
rgba(136,136,136,1) ${y}%,
rgba(136,136,136,0.3) ${Math.min(100, y + spreadRange)}%,
transparent 100%)`;
// 根据鼠标位置判断显示哪些边,使用 opacity 平滑过渡
const topOpacity = y < 50 ? 1 : 0;
const leftOpacity = x < 50 ? 1 : 0;
const rightOpacity = x >= 50 ? 1 : 0;
const bottomOpacity = y >= 50 ? 1 : 0;
return { horizontalGradient, verticalGradient, topOpacity, leftOpacity, rightOpacity, bottomOpacity };
};
return (
<section
ref={sectionRef}
className="bg-black flex flex-col items-center justify-start self-stretch flex-shrink-0 w-full relative"
style={{
padding: '1px 0px 0px 0px',
gap: '40px'
}}
>
<div className="flex flex-col md:flex-row items-stretch justify-start flex-shrink-0 w-full max-w-[1440px] 2xl:max-w-[1760px] 3xl:max-w-[2200px] relative">
{/* 标题区域 */}
<div
className={`flex flex-row items-center justify-center flex-shrink-0 w-full md:w-[459px] 2xl:w-[560px] 3xl:w-[700px] relative transition-all duration-700 ease-out border-b border-[#222222] px-6 py-12 md:py-0 md:pr-6 md:pl-[120px] 2xl:pl-[160px] 3xl:pl-[200px] ${
animate ? 'translate-x-0 opacity-100' : '-translate-x-12 opacity-0'
}`}
>
<div className="flex flex-col gap-6 items-start justify-start flex-1 relative">
<h2
className="text-[#fcfcfd] text-left relative self-stretch font-domine select-none"
style={{
fontSize: 'clamp(36px, 3.5vw, 80px)',
lineHeight: '120%',
letterSpacing: '-0.01em'
}}
>
{t('security.title')}
</h2>
<p
className="text-[#9ca1af] text-left relative flex items-center justify-start font-domine text-base select-none"
style={{
lineHeight: '150%'
}}
>
{t('security.subtitle')}
</p>
</div>
</div>
{/* 卡片区域移动端单列桌面端2x2 */}
<div className="w-full md:flex-1 grid grid-cols-1 md:grid-cols-2 gap-0 relative overflow-visible">
{features.map((feature, index) => {
const borders = getBorderGradients(index);
return (
<div
key={feature.titleKey}
className="relative group h-full overflow-visible"
style={{ zIndex: 0 }}
>
{/* 顶部边框光效 */}
<div
className="absolute left-0 right-0 h-[1px] z-10 transition-opacity duration-300"
style={{ top: '-1px', background: borders?.horizontalGradient, opacity: borders?.topOpacity ?? 0, pointerEvents: 'none' }}
/>
{/* 右边边框光效 */}
<div
className="absolute top-0 right-0 bottom-0 w-[1px] z-10 transition-opacity duration-300"
style={{ background: borders?.verticalGradient, opacity: borders?.rightOpacity ?? 0, pointerEvents: 'none' }}
/>
{/* 底部边框光效 */}
<div
className="absolute bottom-0 left-0 right-0 h-[1px] z-10 transition-opacity duration-300"
style={{ background: borders?.horizontalGradient, opacity: borders?.bottomOpacity ?? 0, pointerEvents: 'none' }}
/>
{/* 左边边框光效 */}
<div
className="absolute top-0 left-0 bottom-0 w-[1px] z-10 transition-opacity duration-300"
style={{ background: borders?.verticalGradient, opacity: borders?.leftOpacity ?? 0, pointerEvents: 'none' }}
/>
<Card
className={`rounded-none bg-black border-[#222222] transition-all duration-300 ${
animate ? 'translate-y-0 opacity-100' : 'translate-y-12 opacity-0'
}`}
shadow="none"
style={{
borderStyle: 'solid',
borderWidth: '0px 0px 1px 0px',
height: '100%',
transitionDelay: animate ? `${index * 150}ms` : '0ms'
}}
onMouseMove={(e: React.MouseEvent<HTMLDivElement>) => handleCardMouseMove(e, index)}
onMouseLeave={() => handleCardMouseLeave(index)}
>
<CardBody className="p-8 md:p-[72px_48px] flex items-start justify-center overflow-hidden">
{feature.special ? (
<div className="flex flex-col gap-6 items-start justify-start self-stretch">
<div className="flex flex-col gap-4 items-start justify-start self-stretch">
<h3
className="text-[#fcfcfd] text-left relative self-stretch font-domine"
style={{ fontSize: 'clamp(18px, 1.5vw, 28px)', lineHeight: '140%' }}
>
{t(feature.titleKey)}
</h3>
<feature.Icon
size={24}
className="flex-shrink-0 text-[#fcfcfd] transition-transform duration-300 ease-out"
style={{
transform: cardHovers[index] ? 'translateX(6px)' : 'translateX(0px)'
}}
/>
</div>
</div>
) : (
<div className="flex flex-col gap-6 items-start justify-start self-stretch">
<feature.Icon size={32} className="flex-shrink-0 text-[#fcfcfd]" />
<div className="flex flex-col gap-2 items-start justify-start self-stretch">
<h3
className="text-[#fcfcfd] text-left relative self-stretch font-domine font-bold"
style={{
fontSize: 'clamp(18px, 1.5vw, 28px)',
lineHeight: '130%',
letterSpacing: '-0.005em'
}}
>
{t(feature.titleKey)}
</h3>
<p
className="text-[#9ca1af] text-left relative self-stretch font-domine text-sm"
style={{ lineHeight: '150%' }}
>
{feature.descKey && t(feature.descKey)}
</p>
</div>
</div>
)}
</CardBody>
</Card>
</div>
);
})}
</div>
</div>
<div className="flex-shrink-0 w-full max-w-[1200px] px-6 md:px-0" style={{ height: 'auto' }}>
<Image
src="/logo1.svg"
alt="Logo"
width={1200}
height={187}
className="w-full h-auto object-contain"
/>
</div>
</section>
);
}