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.
936 lines
56 KiB
936 lines
56 KiB
<!DOCTYPE html>
|
|
<html lang="en" class="scroll-smooth">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Microgrid | Smart Energy System</title>
|
|
|
|
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
|
|
<link href="https://fonts.googleapis.com" rel="preconnect"/>
|
|
<link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect"/>
|
|
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;700;900&family=Montserrat:wght@400;500;700;800;900&family=Inter:wght@400;500;700;800;900&family=JetBrains+Mono:wght@500&display=swap" rel="stylesheet"/>
|
|
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/11.0.5/swiper-bundle.min.css" />
|
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/aos/2.3.4/aos.css" rel="stylesheet">
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/vanilla-tilt/1.7.2/vanilla-tilt.min.js"></script>
|
|
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
|
<script id="tailwind-config">
|
|
tailwind.config = {
|
|
darkMode: "class",
|
|
theme: {
|
|
extend: {
|
|
colors: {
|
|
"primary": "#13eca4",
|
|
"primary-dark": "#0da670", // Added for contrast on light mode
|
|
"background-light": "#f8fcfa",
|
|
"background-dark": "#10221c",
|
|
"pearlescent": "#f0f7f5",
|
|
"silver-accent": "#e2e8f0",
|
|
"accent": "#ff6b00"
|
|
},
|
|
fontFamily: {
|
|
"display": ["Montserrat", "sans-serif"],
|
|
"sans": ["Montserrat", "sans-serif"],
|
|
"mono": ["JetBrains Mono", "Montserrat"],
|
|
"inter": ["Inter", "Montserrat"],
|
|
},
|
|
backgroundImage: {
|
|
'liquid-gradient': 'linear-gradient(135deg, #f8fcfa 0%, #e8f5f1 50%, #dcfce7 100%)',
|
|
'glass-gradient': 'linear-gradient(180deg, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0.4) 100%)',
|
|
'aurora': 'linear-gradient(135deg, rgba(19,236,164,0.15) 0%, rgba(13,166,112,0.08) 50%, rgba(248,252,250,0) 100%)',
|
|
},
|
|
boxShadow: {
|
|
'glow': '0 0 20px rgba(19, 236, 164, 0.4)',
|
|
'bento': '0 20px 40px -15px rgba(0,0,0,0.05), 0 0 0 1px rgba(255,255,255,0.6)',
|
|
'bento-hover': '0 30px 60px -20px rgba(19,236,164,0.25), 0 0 0 1px rgba(19,236,164,0.5)',
|
|
'glass': '0 8px 32px 0 rgba(13, 166, 112, 0.1)',
|
|
},
|
|
keyframes: {
|
|
fadeIn: {
|
|
'0%': { opacity: '0', transform: 'translateY(10px)' },
|
|
'100%': { opacity: '1', transform: 'translateY(0)' },
|
|
},
|
|
dash: {
|
|
'0%': { strokeDashoffset: '1000' },
|
|
'100%': { strokeDashoffset: '0' },
|
|
}
|
|
},
|
|
animation: {
|
|
'fade-in': 'fadeIn 0.5s ease-out forwards',
|
|
'dash-flow': 'dash 3s linear infinite',
|
|
}
|
|
},
|
|
},
|
|
}
|
|
</script>
|
|
</head>
|
|
<body class="bg-liquid-gradient text-background-dark font-sans antialiased overflow-x-hidden selection:bg-primary selection:text-white min-h-screen">
|
|
|
|
<!-- Three.js Canvas Container -->
|
|
<div id="canvas-container" class="fixed inset-0 z-0 pointer-events-none opacity-80"></div>
|
|
<div class="fixed inset-0 bg-gradient-to-t from-background-light via-background-light/40 to-transparent z-0 pointer-events-none"></div>
|
|
|
|
<main class="relative z-10 mx-auto ">
|
|
|
|
<!-- === Hero Section === -->
|
|
<section id="mod-hero" class="min-h-screen flex flex-col justify-center text-center">
|
|
<span class="text-primary font-mono text-sm tracking-[0.3em] uppercase mb-6 " data-aos="fade-down">
|
|
Smart Energy System
|
|
</span>
|
|
<h1 class="text-4xl md:text-[2.66rem] leading-tight tracking-tighter text-background-dark mb-8 drop-shadow-sm" data-aos="zoom-in">
|
|
INTEGRATED INDEPENDENT
|
|
<span class="text-primary">INTELLIGENT.</span>
|
|
</h1>
|
|
<p class="text-[1rem] text-background-dark/70 font-medium max-w-4xl mx-auto leading-relaxed md:text-center" data-aos="fade-up">
|
|
The Next Generation of Microgrid Solutions Empowering industrial parks and zero-carbon communities with seamless, green, and autonomous energy.
|
|
</p>
|
|
</section>
|
|
|
|
<!-- === 系统拓扑图模块 === -->
|
|
<section id="mod-topology" class="min-h-screen flex flex-col justify-center items-center text-center py-24 relative z-10 overflow-hidden">
|
|
|
|
<!-- 明亮网格背景 -->
|
|
<div class="absolute inset-0 z-0 opacity-[0.4] pointer-events-none hex-bg"></div>
|
|
<div class="absolute inset-0 z-0 opacity-[0.5] pointer-events-none" style="background-image: linear-gradient(rgba(19,236,164,0.2) 1px, transparent 1px), linear-gradient(90deg, rgba(19,236,164,0.2) 1px, transparent 1px); background-size: 60px 60px; transform: perspective(800px) rotateX(60deg) scale(2.5) translateY(-50px); transform-origin: top center;"></div>
|
|
|
|
<style>
|
|
.hex-bg { background-image: url("data:image/svg+xml,%3Csvg width='24' height='40' viewBox='0 0 24 40' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 10L12 3l12 7v14l-12 7-12-7V10z' stroke='rgba(13,166,112,0.1)' fill='none' fill-rule='evenodd'/%3E%3C/svg%3E"); }
|
|
.cyber-clip { clip-path: polygon(0 15px, 15px 0, 100% 0, 100% calc(100% - 15px), calc(100% - 15px) 100%, 0 100%); }
|
|
.cyber-hub { clip-path: polygon(30% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30%); }
|
|
.tilt-card { transform-style: preserve-3d; will-change: transform; transform: perspective(1000px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)) scale3d(1, 1, 1); transition: transform 0.1s cubic-bezier(0.25, 0.46, 0.45, 0.94), border-color 0.4s ease; }
|
|
.tilt-card.reset { transition: transform 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94), border-color 0.4s ease; }
|
|
.svg-circuit-line { fill: none; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
|
|
.flow-packet { stroke-dasharray: 15, 150; animation: dashFlow 3s linear infinite; }
|
|
@keyframes dashFlow { 0% { stroke-dashoffset: 165; } 100% { stroke-dashoffset: 0; } }
|
|
.layer-stats { opacity: 1; transition: opacity 0.3s ease, transform 0.3s ease; transform: translateY(0); }
|
|
.layer-text { opacity: 0; pointer-events: none; transition: opacity 0.3s ease, transform 0.3s ease; transform: translateY(10px); }
|
|
.tilt-card:hover .layer-stats { opacity: 0; transform: translateY(-10px); pointer-events: none; }
|
|
.tilt-card:hover .layer-text { opacity: 1; pointer-events: auto; transform: translateY(0); }
|
|
</style>
|
|
|
|
<div class="relative z-30 mb-20" data-aos="fade-down">
|
|
<h2 class="text-4xl md:text-[2.66rem] mb-4 uppercase tracking-[0.2em] text-background-dark flex items-center justify-center gap-4">
|
|
CORE ARCHITECTURE OF MICROGRID SYSTEM
|
|
</h2>
|
|
<div class="flex items-center justify-center gap-4 text-primary font-mono text-xs tracking-[0.4em] ">
|
|
<span class="w-8 h-[2px] bg-gradient-to-r from-transparent to-primary-dark"></span>
|
|
INTELLIGENT MICROGRID NETWORK
|
|
<span class="w-8 h-[2px] bg-gradient-to-l from-transparent to-primary-dark"></span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="relative w-full max-w-[1400px] mx-auto z-30 flex items-center justify-center px-10 lg:px-20 min-h-[500px]">
|
|
|
|
<div class="hidden lg:block absolute inset-0 z-0 pointer-events-none">
|
|
<svg class="w-full h-full drop-shadow-[0_0_8px_rgba(19,236,164,0.4)]" preserveAspectRatio="xMidYMid meet" viewBox="0 0 1400 500">
|
|
<path d="M 380 130 L 450 130 Q 480 130 480 160 L 480 250 L 550 250" class="svg-circuit-line stroke-primary/30" />
|
|
<path d="M 380 130 L 450 130 Q 480 130 480 160 L 480 250 L 550 250" class="svg-circuit-line stroke-primary-dark flow-packet" />
|
|
<path d="M 380 370 L 450 370 Q 480 370 480 340 L 480 250 L 550 250" class="svg-circuit-line stroke-accent/30" />
|
|
<path d="M 380 370 L 450 370 Q 480 370 480 340 L 480 250 L 550 250" class="svg-circuit-line stroke-accent flow-packet" style="animation-duration: 2.5s; animation-direction: reverse;" />
|
|
<path d="M 850 250 L 1020 250" class="svg-circuit-line stroke-primary/30" />
|
|
<path d="M 850 250 L 1020 250" class="svg-circuit-line stroke-primary-dark flow-packet" style="stroke-dasharray: 20, 100; animation-duration: 1.5s;" />
|
|
</svg>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 lg:grid-cols-[minmax(300px,1.2fr)_auto_minmax(300px,1.2fr)] gap-8 lg:gap-16 w-full relative z-10 items-center">
|
|
|
|
<div class="flex flex-col gap-8 relative z-10">
|
|
<!-- Card 1 -->
|
|
<div class="tilt-card relative bg-white/70 backdrop-blur-xl border border-white p-8 cyber-clip cursor-pointer hover:border-primary/50 shadow-glass text-left w-full lg:w-[290px] ml-auto group">
|
|
<div class="flex items-start justify-between mb-6 relative z-10">
|
|
<div class="flex items-center gap-4">
|
|
<div class="w-12 h-12 bg-primary/10 rounded flex items-center justify-center border border-primary/30 text-primary group-hover:bg-primary group-hover:text-white transition-colors duration-300 shadow-sm">
|
|
<span class="material-symbols-outlined text-2xl"></span>
|
|
</div>
|
|
<div>
|
|
<h3 class="text-xl text-background-dark tracking-wide">Power Gen</h3>
|
|
<div class="text-[10px] text-primary font-mono mt-1 ">NODE: GEN_01</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="relative h-[80px] w-full">
|
|
<div class="layer-stats absolute inset-0 w-full grid grid-cols-2 gap-3 font-mono text-xs">
|
|
<div class="bg-primary/5 border border-primary/20 p-3 text-primary flex flex-col justify-center rounded-sm">
|
|
<span class="text-background-dark/50 text-[10px] mb-1 ">VOLTAGE OUT</span>
|
|
<span class="text-base ">400V DC</span>
|
|
</div>
|
|
<div class="bg-primary/5 border border-primary/20 p-3 text-primary flex flex-col justify-center rounded-sm">
|
|
<span class="text-background-dark/50 text-[10px] mb-1 ">STATUS</span>
|
|
<span class="text-base animate-pulse">STABLE</span>
|
|
</div>
|
|
</div>
|
|
<div class="layer-text absolute inset-0 w-full overflow-hidden">
|
|
<p class="decrypt-text text-[13px] text-background-dark/70 font-medium leading-relaxed" data-target="Harnessing renewable and conventional energy for consistent supply.">>_ Awaiting command...</p>
|
|
</div>
|
|
</div>
|
|
<div class="absolute bottom-0 right-0 w-6 h-6 border-b-2 border-r-2 border-primary/40 group-hover:border-primary-dark transition-colors m-1"></div>
|
|
</div>
|
|
|
|
<!-- Card 2 -->
|
|
<div class="tilt-card relative bg-white/70 backdrop-blur-xl border border-white p-8 cyber-clip cursor-pointer hover:border-accent/50 shadow-glass text-left w-full lg:w-[290px] ml-auto group">
|
|
<div class="flex items-start justify-between mb-6 relative z-10">
|
|
<div class="flex items-center gap-4">
|
|
<div class="w-12 h-12 bg-accent/10 rounded flex items-center justify-center border border-accent/30 text-accent group-hover:bg-accent group-hover:text-white transition-colors duration-300 shadow-sm">
|
|
<span class="material-symbols-outlined text-2xl"></span>
|
|
</div>
|
|
<div>
|
|
<h3 class="text-xl text-background-dark tracking-wide">Energy Storage</h3>
|
|
<div class="text-[10px] text-accent font-mono mt-1 ">NODE: BATT_X</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="relative h-[80px] w-full">
|
|
<div class="layer-stats absolute inset-0 w-full grid grid-cols-2 gap-3 font-mono text-xs">
|
|
<div class="bg-accent/5 border border-accent/20 p-3 text-accent flex flex-col justify-center rounded-sm">
|
|
<span class="text-background-dark/50 text-[10px] mb-1 ">CAPACITY</span>
|
|
<span class="text-base ">500 kWh</span>
|
|
</div>
|
|
<div class="bg-accent/5 border border-accent/20 p-3 text-accent flex flex-col justify-center rounded-sm">
|
|
<span class="text-background-dark/50 text-[10px] mb-1 ">MODE</span>
|
|
<span class="text-base animate-pulse">CHARGE</span>
|
|
</div>
|
|
</div>
|
|
<div class="layer-text absolute inset-0 w-full overflow-hidden">
|
|
<p class="decrypt-text text-[13px] text-background-dark/70 font-medium leading-relaxed" data-target="Storing surplus electricity to stabilize the grid day and night.">>_ Interfacing...</p>
|
|
</div>
|
|
</div>
|
|
<div class="absolute bottom-0 right-0 w-6 h-6 border-b-2 border-r-2 border-accent/40 group-hover:border-accent transition-colors m-1"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Central Hub -->
|
|
<div class="flex justify-center items-center relative z-20 mx-4 py-8 lg:py-0">
|
|
<div class="relative flex flex-col items-center justify-center p-12 bg-white/80 backdrop-blur-2xl border-2 border-white cyber-hub hover:border-primary/50 transition-all duration-500 w-[300px] h-[360px] shadow-glass group">
|
|
<div class="relative w-32 h-32 flex items-center justify-center mb-8">
|
|
<div class="absolute inset-0 rounded-full border border-dashed border-primary-dark/40 animate-[spin_10s_linear_infinite]"></div>
|
|
<div class="absolute inset-2 rounded-full border-t-4 border-primary/80 animate-[spin_3s_linear_infinite]"></div>
|
|
<div class="absolute inset-6 rounded-full bg-white border border-primary/30 flex items-center justify-center overflow-hidden z-10 group-hover:shadow-[0_0_20px_rgba(19,236,164,0.6)] transition-shadow duration-500">
|
|
<span class="material-symbols-outlined text-primary text-4xl"></span>
|
|
</div>
|
|
</div>
|
|
<div class="bg-primary/10 border border-primary/30 px-3 py-1 rounded-full text-primary font-mono text-[10px] mb-4 flex gap-2 items-center tracking-widest ">
|
|
<span class="w-2 h-2 bg-primary rounded-full animate-ping"></span> CONVERTER
|
|
</div>
|
|
<h3 class="text-2xl text-background-dark tracking-wide mb-2 z-10 group-hover:text-primary transition-colors">Power Conversion</h3>
|
|
<p class="text-[12px] text-background-dark/60 font-mono tracking-widest text-center mt-2 ">AC/DC ⇌ DC/AC<br>EFF: <span class="text-primary">99.1%</span></p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Card 3 -->
|
|
<div class="flex flex-col justify-center relative z-10">
|
|
<div class="tilt-card relative bg-white/70 backdrop-blur-xl border border-white p-8 cyber-clip cursor-pointer hover:border-primary-dark/50 shadow-glass text-left w-full lg:w-[290px] group">
|
|
<div class="flex items-start justify-between mb-6 relative z-10">
|
|
<div class="flex items-center gap-4">
|
|
<div class="w-12 h-12 bg-primary-dark/10 rounded flex items-center justify-center border border-primary-dark/30 text-primary group-hover:bg-primary-dark group-hover:text-white transition-colors duration-300 shadow-sm">
|
|
<span class="material-symbols-outlined text-2xl"></span>
|
|
</div>
|
|
<div>
|
|
<h3 class="text-xl text-background-dark tracking-wide">Demand Mgt</h3>
|
|
<div class="text-[10px] text-background-dark/50 font-mono mt-1 ">NODE: LOAD_END</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="relative h-[80px] w-full">
|
|
<div class="layer-stats absolute inset-0 w-full grid grid-cols-2 gap-3 font-mono text-xs">
|
|
<div class="bg-gray-100 border border-gray-200 p-3 text-background-dark flex flex-col justify-center rounded-sm">
|
|
<span class="text-background-dark/50 text-[10px] mb-1 ">REAL POWER</span>
|
|
<span class="text-base ">120 kW</span>
|
|
</div>
|
|
<div class="bg-gray-100 border border-gray-200 p-3 text-background-dark flex flex-col justify-center rounded-sm">
|
|
<span class="text-background-dark/50 text-[10px] mb-1 ">POWER FACTOR</span>
|
|
<span class="text-base ">0.98</span>
|
|
</div>
|
|
</div>
|
|
<div class="layer-text absolute inset-0 w-full overflow-hidden">
|
|
<p class="decrypt-text text-[13px] text-background-dark/70 font-medium leading-relaxed" data-target="Distributing power smartly to meet diverse load requirements.">>_ Connecting...</p>
|
|
</div>
|
|
</div>
|
|
<div class="absolute bottom-0 right-0 w-6 h-6 border-b-2 border-r-2 border-primary-dark/40 group-hover:border-primary-dark transition-colors m-1"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Features Sections (Glassmorphism & Bright Theme) -->
|
|
<section id="mod-security" class="min-h-screen flex items-center">
|
|
<div class="max-w-xl bg-white/80 backdrop-blur-xl p-10 border-l-4 border-accent rounded-r-3xl shadow-glass">
|
|
<span class="material-symbols-outlined text-5xl text-accent mb-6"></span>
|
|
<h2 class="text-4xl mb-4 text-background-dark">Safe &<br>Reliable</h2>
|
|
<p class="text-lg text-background-dark/70 leading-relaxed font-medium">Sustains island operation without disconnection, backed by multi-power redundancy to guarantee zero downtime for critical loads.</p>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="mod-efficiency" class="min-h-screen flex items-center justify-end text-right">
|
|
<div class="max-w-xl bg-white/80 backdrop-blur-xl p-10 border-r-4 border-primary rounded-l-3xl shadow-glass">
|
|
<span class="material-symbols-outlined text-5xl text-primary mb-6"></span>
|
|
<h2 class="text-4xl mb-4 text-background-dark">Smart &<br>Efficient</h2>
|
|
<p class="text-lg text-background-dark/70 leading-relaxed font-medium">Intelligent optimization and dispatch enable peak shaving and valley filling, effectively reducing electricity costs and energy consumption.</p>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="mod-netzero" class="min-h-screen flex items-center">
|
|
<div class="max-w-xl bg-white/80 backdrop-blur-xl p-10 border-l-4 border-primary-dark rounded-r-3xl shadow-glass">
|
|
<span class="material-symbols-outlined text-5xl text-primary mb-6"></span>
|
|
<h2 class="text-4xl mb-4 text-background-dark">Green &<br>Low-Carbon</h2>
|
|
<p class="text-lg text-background-dark/70 leading-relaxed font-medium">Efficiently absorbs renewable energy and reduces carbon emissions, facilitating the realization of dual-carbon goals.</p>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="mod-autonomous" class="min-h-screen flex items-center justify-end text-right">
|
|
<div class="max-w-xl bg-white/80 backdrop-blur-xl p-10 border-r-4 border-accent rounded-l-3xl shadow-glass">
|
|
<span class="material-symbols-outlined text-5xl text-accent mb-6"></span>
|
|
<h2 class="text-4xl mb-4 text-background-dark">Autonomous &<br>Controllable</h2>
|
|
<p class="text-lg text-background-dark/70 leading-relaxed font-medium">Operates independently of the external grid, significantly enhancing energy autonomy and risk resilience.</p>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- === Scenarios === -->
|
|
<section id="mod-scenarios" class="min-h-[80vh] flex flex-col justify-center items-center text-center pb-20 relative z-10" style="perspective: 2000px;">
|
|
<h2 class="text-4xl md:text-[2.66rem] mb-12 uppercase tracking-widest text-background-dark mt-10 relative z-30">Applicable Scenarios</h2>
|
|
|
|
<div class="flex flex-wrap justify-center gap-4 font-mono text-sm max-w-4xl relative z-30 " id="scenario-buttons">
|
|
<button class="scenario-btn px-6 py-2 border border-primary/20 bg-white/80 hover:border-primary-dark hover:text-primary transition-all duration-300 rounded-full shadow-sm text-background-dark/70">Industrial Parks</button>
|
|
<button class="scenario-btn px-6 py-2 border border-primary/20 bg-white/80 hover:border-primary-dark hover:text-primary transition-all duration-300 rounded-full shadow-sm text-background-dark/70">Data Centers</button>
|
|
<button class="scenario-btn px-6 py-2 border border-primary/20 bg-white/80 hover:border-primary-dark hover:text-primary transition-all duration-300 rounded-full shadow-sm text-background-dark/70">Commercial Complexes</button>
|
|
<button class="scenario-btn px-6 py-2 border border-primary/20 bg-white/80 hover:border-primary-dark hover:text-primary transition-all duration-300 rounded-full shadow-sm text-background-dark/70">Hospitals & Schools</button>
|
|
<button class="scenario-btn px-6 py-2 border border-primary/20 bg-white/80 hover:border-primary-dark hover:text-primary transition-all duration-300 rounded-full shadow-sm text-background-dark/70">Remote Regions</button>
|
|
<button class="scenario-btn px-6 py-2 border border-primary/20 bg-white/80 hover:border-primary-dark hover:text-primary transition-all duration-300 rounded-full shadow-sm text-background-dark/70">Islands</button>
|
|
<button class="scenario-btn active-scenario px-6 py-2 bg-primary/10 border border-primary text-primary shadow-[0_4px_15px_rgba(19,236,164,0.3)] transition-all duration-300 rounded-full">Zero-Carbon Communities</button>
|
|
<button class="scenario-btn px-6 py-2 border border-primary/20 bg-white/80 hover:border-primary-dark hover:text-primary transition-all duration-300 rounded-full shadow-sm text-background-dark/70">New Energy Stations</button>
|
|
</div>
|
|
|
|
<div id="card-wrapper" class="relative mt-12 w-full max-w-5xl h-[450px] z-20" style="transform-style: preserve-3d;">
|
|
<div id="scenario-card" class="absolute inset-0 w-full h-full rounded-3xl overflow-hidden border-4 border-white shadow-2xl bg-white opacity-0" style="transform: translateZ(-500px) rotateX(20deg); pointer-events: none;">
|
|
<div class="absolute inset-0 z-0 overflow-hidden">
|
|
<img id="card-img" src="" class="w-full h-full object-cover opacity-90" alt="Scenario Image" />
|
|
</div>
|
|
<!-- Light theme inner gradient -->
|
|
<div class="absolute inset-0 bg-gradient-to-t from-white via-white/80 to-transparent z-10"></div>
|
|
<div class="absolute bottom-12 left-12 z-20 text-left">
|
|
<h3 id="card-title" class="text-4xl font-display text-background-dark mb-4 tracking-tighter"></h3>
|
|
<p id="card-desc" class="text-lg text-background-dark/80 max-w-2xl leading-relaxed font-medium"></p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- === Contact === -->
|
|
<section id="mod-contact" class="w-full py-24 relative z-10 overflow-hidden bg-white/50 border-t border-white shadow-[0_-10px_40px_rgba(13,166,112,0.05)]">
|
|
<div class="max-w-7xl mx-auto px-6">
|
|
<div class="flex items-center gap-4 mb-12" data-aos="fade-right">
|
|
<div class="h-[2px] w-12 bg-primary-dark"></div>
|
|
<span class="text-primary font-mono text-xs uppercase tracking-[0.4em] ">Get in Touch</span>
|
|
</div>
|
|
|
|
<div class="flex flex-col lg:flex-row lg:items-end lg:justify-between gap-12">
|
|
<div data-aos="fade-up" data-aos-delay="100">
|
|
<h2 class="text-background-dark/50 text-sm font-mono uppercase tracking-widest mb-2 ">Microgrid Project Leader</h2>
|
|
<div class="flex items-baseline gap-6">
|
|
<span class="text-5xl tracking-tighter text-background-dark">Yan</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 w-full lg:max-w-2xl" data-aos="fade-left" data-aos-delay="200">
|
|
<a href="mailto:sheyanxin@nenghui.com" class="group relative p-6 bg-white border border-primary/20 rounded-2xl overflow-hidden transition-all duration-500 hover:border-primary hover:shadow-lg">
|
|
<div class="absolute inset-0 bg-gradient-to-r from-primary/5 to-transparent translate-x-[-100%] group-hover:translate-x-[0%] transition-transform duration-500"></div>
|
|
<div class="relative flex items-center gap-4">
|
|
<div class="w-12 h-12 rounded-full bg-primary/10 flex items-center justify-center text-primary">
|
|
<span class="material-symbols-outlined"></span>
|
|
</div>
|
|
<div>
|
|
<div class="text-background-dark/50 text-[10px] uppercase tracking-widest ">Send Email</div>
|
|
<div class="text-background-dark group-hover:text-primary transition-colors">sheyanxin@nenghui.com</div>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
|
|
<a href="tel:8619120593729" class="group relative p-6 bg-white border border-primary/20 rounded-2xl overflow-hidden transition-all duration-500 hover:border-primary hover:shadow-lg">
|
|
<div class="absolute inset-0 bg-gradient-to-r from-primary/5 to-transparent translate-x-[-100%] group-hover:translate-x-[0%] transition-transform duration-500"></div>
|
|
<div class="relative flex items-center gap-4">
|
|
<div class="w-12 h-12 rounded-full bg-primary/10 flex items-center justify-center text-primary">
|
|
<span class="material-symbols-outlined"></span>
|
|
</div>
|
|
<div>
|
|
<div class="text-background-dark/50 text-[10px] uppercase tracking-widest ">Call Directly</div>
|
|
<div class="text-background-dark group-hover:text-primary transition-colors">+86 191 2059 3729</div>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/aos/2.3.4/aos.js"></script>
|
|
|
|
<!-- JavaScript Logic & Animations -->
|
|
<script>
|
|
AOS.init({ once: true });
|
|
gsap.registerPlugin(ScrollTrigger);
|
|
|
|
// --- 卡片倾斜与打字机特效 (保持不变) ---
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
const cards = document.querySelectorAll('.tilt-card');
|
|
cards.forEach(card => {
|
|
card.addEventListener('mousemove', e => {
|
|
card.classList.remove('reset');
|
|
const rect = card.getBoundingClientRect();
|
|
const x = e.clientX - rect.left;
|
|
const y = e.clientY - rect.top;
|
|
const centerX = rect.width / 2;
|
|
const centerY = rect.height / 2;
|
|
const rotateX = ((y - centerY) / centerY) * -4;
|
|
const rotateY = ((x - centerX) / centerX) * 4;
|
|
card.style.setProperty('--rx', `${rotateX}deg`);
|
|
card.style.setProperty('--ry', `${rotateY}deg`);
|
|
});
|
|
|
|
card.addEventListener('mouseleave', () => {
|
|
card.classList.add('reset');
|
|
card.style.setProperty('--rx', `0deg`);
|
|
card.style.setProperty('--ry', `0deg`);
|
|
const decryptEl = card.querySelector('.decrypt-text');
|
|
if(decryptEl) {
|
|
decryptEl.dataset.isAnimating = "false";
|
|
decryptEl.innerHTML = `>_ Awaiting command...`;
|
|
}
|
|
});
|
|
|
|
card.addEventListener('mouseenter', () => {
|
|
const decryptEl = card.querySelector('.decrypt-text');
|
|
if(!decryptEl || decryptEl.dataset.isAnimating === "true") return;
|
|
decryptEl.dataset.isAnimating = "true";
|
|
const targetText = decryptEl.getAttribute('data-target');
|
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#$%&*';
|
|
let iteration = 0;
|
|
function decryptFrame() {
|
|
if(decryptEl.dataset.isAnimating === "false") return;
|
|
decryptEl.innerHTML = targetText.split("").map((letter, index) => {
|
|
if(index < iteration) return letter;
|
|
return chars[Math.floor(Math.random() * chars.length)];
|
|
}).join("") + `<span class="animate-pulse text-primary ml-1">_</span>`;
|
|
if(iteration < targetText.length) {
|
|
iteration += 1;
|
|
setTimeout(() => requestAnimationFrame(decryptFrame), 15);
|
|
} else {
|
|
decryptEl.dataset.isAnimating = "false";
|
|
decryptEl.innerHTML = targetText + `<span class="animate-pulse text-primary ml-1">_</span>`;
|
|
}
|
|
}
|
|
requestAnimationFrame(decryptFrame);
|
|
});
|
|
});
|
|
});
|
|
|
|
// --- Three.js 亮色主题粒子引擎 ---
|
|
const scene = new THREE.Scene();
|
|
// 增加雾化效果,让远处的粒子融入背景
|
|
scene.fog = new THREE.FogExp2(0xf8fcfa, 0.0015);
|
|
|
|
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 2000);
|
|
camera.position.z = 500;
|
|
|
|
function updateCameraFOV() {
|
|
camera.fov = window.innerWidth < 768 ? 75 : 55;
|
|
camera.updateProjectionMatrix();
|
|
}
|
|
updateCameraFOV();
|
|
|
|
const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
|
|
renderer.setPixelRatio(window.devicePixelRatio);
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
document.getElementById('canvas-container').appendChild(renderer.domElement);
|
|
|
|
const particleCount = window.innerWidth < 768 ? 9000 : 12000;
|
|
const geometry = new THREE.BufferGeometry();
|
|
|
|
const currentPositions = new Float32Array(particleCount * 3);
|
|
const renderPositions = new Float32Array(particleCount * 3);
|
|
const startPos = new Float32Array(particleCount * 3);
|
|
const targetPos = new Float32Array(particleCount * 3);
|
|
const colors = new Float32Array(particleCount * 3);
|
|
|
|
// --- 核心改动:适合亮色背景的粒子颜色 ---
|
|
const colorPrimary = new THREE.Color('#09ab75'); // 深翠绿,保证在白底上清晰
|
|
const colorAccent = new THREE.Color('#64e8bc'); // 亮绿色用于点缀
|
|
const colorGray = new THREE.Color('#94a3b8'); // 蓝灰色替代原有的银白色
|
|
|
|
for (let i = 0; i < particleCount; i++) {
|
|
const mix = Math.random();
|
|
const color = colorPrimary.clone().lerp(colorAccent, mix * 0.5);
|
|
colors.set([color.r, color.g, color.b], i * 3);
|
|
}
|
|
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
|
|
|
|
// 形状生成器 (保持原有计算逻辑)
|
|
const shapesGen = {
|
|
text: () => {
|
|
const canvas = document.createElement('canvas');
|
|
canvas.width = 1200; canvas.height = 300;
|
|
const ctx = canvas.getContext('2d');
|
|
ctx.fillStyle = '#000'; ctx.fillRect(0, 0, 1200, 300);
|
|
ctx.font = 'bold 200px "Arial", sans-serif';
|
|
ctx.fillStyle = '#fff'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
|
|
ctx.fillText( window.innerWidth < 768 ? 'NH' :'NENGHUI', 600, 150);
|
|
const data = ctx.getImageData(0, 0, 1200, 300).data;
|
|
let points = [];
|
|
for (let y = 0; y < 300; y += 3) {
|
|
for (let x = 0; x < 1200; x += 3) {
|
|
if (data[(y * 1200 + x) * 4] > 128) {
|
|
points.push({ x: x - 600, y: -(y - 150), z: (Math.random() - 0.5) * 20 });
|
|
}
|
|
}
|
|
}
|
|
const arr = new Float32Array(particleCount * 3);
|
|
for (let i = 0; i < particleCount; i++) {
|
|
const p = points[i % points.length];
|
|
arr.set([p.x, p.y, p.z], i * 3);
|
|
}
|
|
return arr;
|
|
},
|
|
nestedPolyhedron: () => {
|
|
const arr = new Float32Array(particleCount * 3);
|
|
for (let i = 0; i < particleCount; i++) {
|
|
const r = i % 2 === 0 ? 100 : 50;
|
|
const theta = Math.random() * Math.PI * 2;
|
|
const phi = Math.acos(Math.random() * 2 - 1);
|
|
let x = r * Math.sin(phi) * Math.cos(theta);
|
|
let y = r * Math.sin(phi) * Math.sin(theta);
|
|
let z = r * Math.cos(phi);
|
|
if (Math.abs(x) > r * 0.7) x = Math.sign(x) * r * 0.7;
|
|
arr.set([x, y, z], i * 3);
|
|
}
|
|
return arr;
|
|
},
|
|
symbioticLeaf: () => {
|
|
const arr = new Float32Array(particleCount * 3);
|
|
const leafParticles = Math.floor(particleCount * 0.85);
|
|
const carbonParticles = particleCount - leafParticles;
|
|
for (let i = 0; i < leafParticles; i++) {
|
|
const t = Math.random();
|
|
const y = t * 400 - 150;
|
|
const widthFactor = Math.sin(t * Math.PI) * (1.2 - 0.2 * t);
|
|
const maxWidth = 160;
|
|
let x, z;
|
|
const isVein = Math.random() < 0.4;
|
|
if (isVein) {
|
|
if (Math.random() < 0.3) {
|
|
x = (Math.random() - 0.5) * 4;
|
|
z = (Math.random() - 0.5) * 4;
|
|
} else {
|
|
const side = Math.random() > 0.5 ? 1 : -1;
|
|
const veinT = Math.floor(t * 10) / 10;
|
|
x = side * (t - veinT) * 300 + (side * veinT * 20);
|
|
x = THREE.MathUtils.clamp(x, -maxWidth * widthFactor, maxWidth * widthFactor);
|
|
z = (Math.random() - 0.5) * 10;
|
|
}
|
|
} else {
|
|
const rawX = (Math.random() - 0.5) * 2 * maxWidth * widthFactor;
|
|
x = Math.round(rawX / 15) * 15;
|
|
z = (Math.random() - 0.5) * 20;
|
|
}
|
|
const bend = Math.sin(t * Math.PI) * 30;
|
|
arr.set([x + bend, y, z], i * 3);
|
|
}
|
|
for (let i = 0; i < carbonParticles; i++) {
|
|
const idx = (leafParticles + i) * 3;
|
|
arr.set([
|
|
(Math.random() - 0.5) * 300,
|
|
-250 - Math.random() * 150,
|
|
(Math.random() - 0.5) * 60
|
|
], idx);
|
|
}
|
|
return arr;
|
|
},
|
|
concentricSphere: () => {
|
|
const arr = new Float32Array(particleCount * 3);
|
|
for (let i = 0; i < particleCount; i++) {
|
|
const r = i % 3 === 0 ? 140 : (i % 3 === 1 ? 90 : 40);
|
|
const phi = Math.acos(-1 + Math.random() * 2);
|
|
const theta = Math.random() * Math.PI * 2;
|
|
arr.set([r * Math.sin(phi) * Math.cos(theta), r * Math.sin(phi) * Math.sin(theta), r * Math.cos(phi)], i * 3);
|
|
}
|
|
return arr;
|
|
},
|
|
barGraph: () => {
|
|
const arr = new Float32Array(particleCount * 3);
|
|
for (let i = 0; i < particleCount; i++) {
|
|
const gridX = Math.floor(Math.random() * 8) - 4;
|
|
const gridZ = Math.floor(Math.random() * 8) - 4;
|
|
const height = (Math.sin(gridX) + Math.cos(gridZ) + 2) * 40 + 20;
|
|
arr.set([gridX * 40 + (Math.random() - 0.5) * 20, Math.random() * height - 100, gridZ * 40 + (Math.random() - 0.5) * 20], i * 3);
|
|
}
|
|
return arr;
|
|
},
|
|
shield: () => {
|
|
const arr = new Float32Array(particleCount * 3);
|
|
for (let i = 0; i < particleCount; i++) {
|
|
let x = (Math.random() - 0.5) * 200;
|
|
let y, z;
|
|
let topEdge = 100 + 15 * Math.cos(x * Math.PI / 100);
|
|
let bottomEdge = -120 + 220 * Math.pow(Math.abs(x) / 100, 2);
|
|
let t = Math.random();
|
|
if (Math.random() > 0.6) {
|
|
t = Math.random() > 0.5 ? Math.random() * 0.1 : 0.9 + Math.random() * 0.1;
|
|
}
|
|
y = bottomEdge + t * (topEdge - bottomEdge);
|
|
let maxZ = 30 * (1 - Math.pow(Math.abs(x) / 100, 2));
|
|
if (Math.abs(x) < 8 || Math.abs(y - 10) < 8) maxZ += 10;
|
|
z = (Math.random() - 0.5) * maxZ;
|
|
arr.set([x, y, z], i * 3);
|
|
}
|
|
return arr;
|
|
},
|
|
torusKnot: () => {
|
|
const arr = new Float32Array(particleCount * 3);
|
|
for (let i = 0; i < particleCount; i++) {
|
|
const t = Math.random() * Math.PI * 2;
|
|
const p = 2; const q = 3;
|
|
const r = 50 * (2 + Math.cos(q * t));
|
|
const x = r * Math.cos(p * t);
|
|
const y = r * Math.sin(p * t);
|
|
const z = 50 * Math.sin(q * t);
|
|
const noise = 15;
|
|
arr.set([x + (Math.random() - 0.5) * noise, y + (Math.random() - 0.5) * noise, z + (Math.random() - 0.5) * noise], i * 3);
|
|
}
|
|
return arr;
|
|
},
|
|
networkNode: () => {
|
|
const arr = new Float32Array(particleCount * 3);
|
|
const nodes = [
|
|
{x: 0, y: 0, z: 0, r: 35},
|
|
{x: 120, y: 70, z: 40, r: 15},
|
|
{x: -110, y: -80, z: 20, r: 20},
|
|
{x: 90, y: -100, z: -40, r: 15},
|
|
{x: -100, y: 90, z: -30, r: 18},
|
|
{x: 0, y: 130, z: 10, r: 12},
|
|
{x: 0, y: -130, z: 0, r: 12}
|
|
];
|
|
for (let i = 0; i < particleCount; i++) {
|
|
const pType = Math.random();
|
|
if (pType < 0.5) {
|
|
const node = nodes[Math.floor(Math.random() * nodes.length)];
|
|
const theta = Math.random() * Math.PI * 2;
|
|
const phi = Math.acos((Math.random() * 2) - 1);
|
|
const r = node.r * (0.5 + 0.5 * Math.random());
|
|
arr.set([
|
|
node.x + r * Math.sin(phi) * Math.cos(theta),
|
|
node.y + r * Math.sin(phi) * Math.sin(theta),
|
|
node.z + r * Math.cos(phi)
|
|
], i * 3);
|
|
} else {
|
|
const targetNode = nodes[1 + Math.floor(Math.random() * (nodes.length - 1))];
|
|
const t = Math.random();
|
|
const noise = 3;
|
|
arr.set([
|
|
t * targetNode.x + (Math.random() - 0.5) * noise,
|
|
t * targetNode.y + (Math.random() - 0.5) * noise,
|
|
t * targetNode.z + (Math.random() - 0.5) * noise
|
|
], i * 3);
|
|
}
|
|
}
|
|
return arr;
|
|
}
|
|
};
|
|
|
|
const precalculatedShapes = {
|
|
text: shapesGen.text(),
|
|
nestedPolyhedron: shapesGen.nestedPolyhedron(),
|
|
symbioticLeaf: shapesGen.symbioticLeaf(),
|
|
concentricSphere: shapesGen.concentricSphere(),
|
|
barGraph: shapesGen.barGraph(),
|
|
shield: shapesGen.shield(),
|
|
torusKnot: shapesGen.torusKnot(),
|
|
networkNode: shapesGen.networkNode()
|
|
};
|
|
|
|
currentPositions.set(precalculatedShapes.text);
|
|
renderPositions.set(precalculatedShapes.text);
|
|
geometry.setAttribute('position', new THREE.BufferAttribute(renderPositions, 3));
|
|
|
|
// --- 核心改动:必须使用 NormalBlending 才能在白底上显示颜色 ---
|
|
const material = new THREE.PointsMaterial({
|
|
size: window.innerWidth < 768 ? 3.5 : 4.0,
|
|
vertexColors: true,
|
|
transparent: true,
|
|
opacity: 0.8,
|
|
blending: THREE.NormalBlending, // AdditiveBlending 会在白底上完全消失!
|
|
sizeAttenuation: true,
|
|
depthWrite: false
|
|
});
|
|
const particles = new THREE.Points(geometry, material);
|
|
scene.add(particles);
|
|
|
|
let time = 0;
|
|
let morphData = { progress: 0 };
|
|
let currentShapeKey = 'text';
|
|
let morphTween = null;
|
|
|
|
const rotationSpeed = { x: 0, y: 0 };
|
|
const waveConfig = { amplitude: 20 };
|
|
|
|
function morphTo(shapeKey) {
|
|
if (currentShapeKey === shapeKey) return;
|
|
currentShapeKey = shapeKey;
|
|
|
|
if (morphTween) morphTween.kill();
|
|
|
|
const colorAttribute = geometry.attributes.color;
|
|
const targetColors = new Float32Array(particleCount * 3);
|
|
const leafParticles = Math.floor(particleCount * 0.85);
|
|
|
|
for (let i = 0; i < particleCount; i++) {
|
|
let color;
|
|
if (shapeKey === 'symbioticLeaf') {
|
|
if (i < leafParticles) {
|
|
color = colorPrimary.clone().lerp(colorAccent, Math.random() * 0.2);
|
|
} else {
|
|
color = colorGray.clone();
|
|
}
|
|
} else {
|
|
color = colorPrimary.clone().lerp(colorAccent, Math.random() * 0.4);
|
|
}
|
|
targetColors.set([color.r, color.g, color.b], i * 3);
|
|
}
|
|
|
|
gsap.to(colorAttribute.array, {
|
|
endArray: targetColors,
|
|
duration: 1.5,
|
|
ease: "power2.inOut",
|
|
onUpdate: () => colorAttribute.needsUpdate = true
|
|
});
|
|
|
|
for (let i = 0; i < currentPositions.length; i++) {
|
|
startPos[i] = currentPositions[i];
|
|
}
|
|
targetPos.set(precalculatedShapes[shapeKey]);
|
|
|
|
morphData.progress = 0;
|
|
morphTween = gsap.to(morphData, {
|
|
progress: 1,
|
|
duration: 2.0,
|
|
ease: "power3.inOut",
|
|
onUpdate: () => {
|
|
for (let i = 0; i < particleCount * 3; i++) {
|
|
currentPositions[i] = startPos[i] + (targetPos[i] - startPos[i]) * morphData.progress;
|
|
}
|
|
}
|
|
});
|
|
|
|
if (shapeKey === 'text') {
|
|
gsap.to(rotationSpeed, { x: 0, y: 0, duration: 1.5 });
|
|
const targetX = Math.round(particles.rotation.x / (Math.PI * 2)) * (Math.PI * 2);
|
|
const targetY = Math.round(particles.rotation.y / (Math.PI * 2)) * (Math.PI * 2);
|
|
gsap.to(particles.rotation, { x: targetX, y: targetY, duration: 2.0 });
|
|
} else if (shapeKey === 'shield') {
|
|
gsap.to(rotationSpeed, { x: 0, y: 0.002, duration: 1.5 });
|
|
const targetX = Math.round(particles.rotation.x / (Math.PI * 2)) * (Math.PI * 2);
|
|
gsap.to(particles.rotation, { x: targetX, duration: 2.0 });
|
|
} else if (shapeKey === 'symbioticLeaf') {
|
|
gsap.to(rotationSpeed, { x: 0.0005, y: 0.001, duration: 1.5 });
|
|
} else if (shapeKey === 'barGraph') {
|
|
gsap.to(rotationSpeed, { x: 0, y: 0.002, duration: 1.5 });
|
|
const targetX = Math.round(particles.rotation.x / (Math.PI * 2)) * (Math.PI * 2);
|
|
gsap.to(particles.rotation, { x: targetX + 0.4, duration: 2.0 });
|
|
} else {
|
|
gsap.to(rotationSpeed, { x: 0.001, y: 0.002, duration: 2.0 });
|
|
}
|
|
|
|
gsap.to(waveConfig, {
|
|
duration: 2.0,
|
|
amplitude: (shapeKey === 'text') ? 20 : 0
|
|
});
|
|
}
|
|
|
|
function animate() {
|
|
requestAnimationFrame(animate);
|
|
time += 0.02;
|
|
|
|
const positions = geometry.attributes.position.array;
|
|
const colorsArray = geometry.attributes.color.array;
|
|
const leafParticles = Math.floor(particleCount * 0.85);
|
|
|
|
for (let i = 0; i < particleCount; i++) {
|
|
let idx = i * 3;
|
|
let baseX = currentPositions[idx];
|
|
let baseY = currentPositions[idx + 1];
|
|
let baseZ = currentPositions[idx + 2];
|
|
|
|
if (currentShapeKey === 'symbioticLeaf' ) {
|
|
if (i < leafParticles) {
|
|
if (i % 7 === 0 ) {
|
|
if (Math.abs(baseX) < 100) {
|
|
const angle = time * 3 + (baseY * 0.02);
|
|
const r = 10 * Math.sin(time * 0.5);
|
|
positions[idx] = baseX + r * Math.cos(angle);
|
|
positions[idx + 1] = baseY;
|
|
positions[idx + 2] = baseZ + r * Math.sin(angle);
|
|
}
|
|
} else {
|
|
positions[idx] = baseX + Math.sin(time + baseY * 0.1) * 2;
|
|
positions[idx + 1] = baseY;
|
|
positions[idx + 2] = baseZ;
|
|
}
|
|
}
|
|
} else if (waveConfig.amplitude > 0.01) {
|
|
const phase = baseX * 0.006 + time;
|
|
const waveY = Math.sin(phase) * (waveConfig.amplitude * 0.4);
|
|
const waveZ = Math.cos(phase * 0.8) * waveConfig.amplitude;
|
|
positions[idx] = baseX;
|
|
positions[idx + 1] = baseY + waveY;
|
|
positions[idx + 2] = baseZ + waveZ;
|
|
} else {
|
|
positions[idx] = baseX;
|
|
positions[idx + 1] = baseY;
|
|
positions[idx + 2] = baseZ;
|
|
}
|
|
|
|
if (currentShapeKey === 'symbioticLeaf') {
|
|
if (i >= leafParticles) {
|
|
let y = baseY + (time * 50) % 200;
|
|
let opacity = 1.0;
|
|
|
|
if (y > -150) {
|
|
opacity = THREE.MathUtils.lerp(1.0, 0.0, (y + 150) / 100);
|
|
}
|
|
if (y > -50) opacity = 0;
|
|
positions[idx + 1] = y;
|
|
colorsArray[idx] = colorGray.r * opacity;
|
|
colorsArray[idx + 1] = colorGray.g * opacity;
|
|
colorsArray[idx + 2] = colorGray.b * opacity;
|
|
}
|
|
}
|
|
}
|
|
|
|
geometry.attributes.position.needsUpdate = true;
|
|
if (currentShapeKey === 'symbioticLeaf') {
|
|
geometry.attributes.color.needsUpdate = true;
|
|
}
|
|
|
|
particles.rotation.x += rotationSpeed.x;
|
|
particles.rotation.y += rotationSpeed.y;
|
|
|
|
renderer.render(scene, camera);
|
|
}
|
|
animate();
|
|
|
|
window.addEventListener('resize', () => {
|
|
camera.aspect = window.innerWidth / window.innerHeight;
|
|
camera.updateProjectionMatrix();
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
updateCameraFOV();
|
|
});
|
|
|
|
const shapeList = ['text', 'shield', 'concentricSphere', 'symbioticLeaf', 'torusKnot', 'networkNode', 'barGraph', 'nestedPolyhedron'];
|
|
const sections = ['#mod-hero', '#mod-security', '#mod-efficiency', '#mod-netzero', '#mod-autonomous', '#mod-topology', '#mod-scenarios', '#mod-contact'];
|
|
|
|
sections.forEach((id, idx) => {
|
|
ScrollTrigger.create({
|
|
trigger: id,
|
|
start: "top 55%",
|
|
end: "bottom 45%",
|
|
onEnter: () => morphTo(shapeList[idx]),
|
|
onEnterBack: () => morphTo(shapeList[idx])
|
|
});
|
|
});
|
|
|
|
</script>
|
|
<script>
|
|
// --- 场景卡片 3D 动画 ---
|
|
const scenarioData = {
|
|
"Industrial Parks": { img: "https://nenghui.com/wp-content/uploads/2026/03/icrogrid-Industrial-parks.jpg", desc: "Optimize energy consumption and reduce operational costs for smart industrial zones with intelligent management." },
|
|
"Data Centers": { img: "https://nenghui.com/wp-content/uploads/2026/03/icrogrid-data-center.jpg", desc: "Ensuring continuous mission-critical power supply for data centers with robust and resilient microgrids." },
|
|
"Commercial Complexes": { img: "https://nenghui.com/wp-content/uploads/2026/03/icrogrid-Commercial-complexes.jpg", desc: "Optimize operational costs for commercial hubs through intelligent energy distribution and Peak Shaving." },
|
|
"Hospitals & Schools": { img: "https://nenghui.com/wp-content/uploads/2026/03/icrogrid-Hospitals-schools.jpg", desc: "Ensuring uninterrupted power supply for critical medical services and secure educational campus environments." },
|
|
"Remote Regions": { img: "https://nenghui.com/wp-content/uploads/2026/03/icrogrid-Remote-regions.jpg", desc: "Overcoming geographical barriers with advanced microgrid technology to illuminate the world's remotest corners." },
|
|
"Islands": { img: "https://nenghui.com/wp-content/uploads/2026/03/icrogrid-Islands.jpg", desc: "Achieve reliable energy independence for remote islands through sustainable solar and storage systems." },
|
|
"Zero-Carbon Communities": { img: "https://nenghui.com/wp-content/uploads/2026/03/icrogrid-Zero-carbon-communities.jpg", desc: "Building a sustainable future for modern living through intelligent and eco-friendly microgrid systems." },
|
|
"New Energy Stations": { img: "https://nenghui.com/wp-content/uploads/2026/03/icrogrid-New-energy-stations.jpg", desc: "Enhancing grid stability and renewable integration through advanced storage-based microgrid management systems." }
|
|
};
|
|
|
|
const scenarioButtons = document.querySelectorAll('.scenario-btn');
|
|
const scenarioCard = document.getElementById('scenario-card');
|
|
const cardImg = document.getElementById('card-img');
|
|
const cardTitle = document.getElementById('card-title');
|
|
const cardDesc = document.getElementById('card-desc');
|
|
|
|
let currentActiveBtn = document.querySelector('.active-scenario');
|
|
|
|
function updateCardContent(scenarioName) {
|
|
if(!scenarioData[scenarioName]) return;
|
|
const data = scenarioData[scenarioName];
|
|
|
|
const tl = gsap.timeline();
|
|
tl.to(scenarioCard, { rotationY: 90, opacity: 0, duration: 0.4, ease: "power2.in" })
|
|
.call(() => {
|
|
cardImg.src = data.img;
|
|
cardTitle.innerText = scenarioName;
|
|
cardDesc.innerText = data.desc;
|
|
})
|
|
.fromTo(scenarioCard,
|
|
{ rotationY: -90 },
|
|
{ rotationY: 0, opacity: 1, duration: 0.6, ease: "back.out(1.7)" }
|
|
);
|
|
}
|
|
|
|
scenarioButtons.forEach(btn => {
|
|
btn.addEventListener('click', () => {
|
|
if (btn === currentActiveBtn) return;
|
|
|
|
scenarioButtons.forEach(b => {
|
|
b.classList.remove('active-scenario', 'bg-primary/10', 'border-primary', 'text-primary', 'shadow-[0_4px_15px_rgba(19,236,164,0.3)]');
|
|
b.classList.add('border-primary/20', 'bg-white/80', 'text-background-dark/70');
|
|
});
|
|
btn.classList.add('active-scenario', 'bg-primary/10', 'border-primary', 'text-primary', 'shadow-[0_4px_15px_rgba(19,236,164,0.3)]');
|
|
|
|
currentActiveBtn = btn;
|
|
updateCardContent(btn.innerText.trim());
|
|
});
|
|
});
|
|
|
|
if (currentActiveBtn) {
|
|
const data = scenarioData[currentActiveBtn.innerText.trim()];
|
|
if (data) {
|
|
cardImg.src = data.img;
|
|
cardTitle.innerText = currentActiveBtn.innerText.trim();
|
|
cardDesc.innerText = data.desc;
|
|
}
|
|
}
|
|
|
|
ScrollTrigger.create({
|
|
trigger: '#mod-scenarios',
|
|
start: "top 60%",
|
|
end: "bottom top",
|
|
onEnter: () => {
|
|
gsap.fromTo(scenarioCard,
|
|
{ z: -1000, rotationX: 45, rotationY: -30, opacity: 0, scale: 0.5 },
|
|
{ z: 0, rotationX: 0, rotationY: 0, opacity: 1, scale: 1, duration: 1.5, ease: "expo.out" }
|
|
);
|
|
},
|
|
onLeaveBack: () => {
|
|
gsap.to(scenarioCard, {
|
|
z: -800, rotationX: 20, opacity: 0, scale: 0.7, duration: 0.8, ease: "power2.in"
|
|
});
|
|
},
|
|
onLeave: () => {
|
|
gsap.to(scenarioCard, {
|
|
y: -200, z: 200, rotationX: -20, opacity: 0, duration: 0.8, ease: "power2.in"
|
|
});
|
|
},
|
|
onEnterBack: () => {
|
|
gsap.to(scenarioCard, {
|
|
y: 0, z: 0, rotationX: 0, opacity: 1, scale: 1, duration: 1, ease: "back.out(1.2)"
|
|
});
|
|
}
|
|
});
|
|
|
|
</script>
|
|
</body>
|
|
</html>
|