diff --git a/src/components/effects/advanced-floating-effects.tsx b/src/components/effects/advanced-floating-effects.tsx new file mode 100644 index 0000000..f0e3fdc --- /dev/null +++ b/src/components/effects/advanced-floating-effects.tsx @@ -0,0 +1,450 @@ +'use client'; + +import { motion, useScroll, useTransform } from 'framer-motion'; +import { useMemo, useState, useEffect, useRef } from 'react'; +import { Cpu, Shield, Zap, Globe, FileText, TrendingUp, BarChart3, Users } from 'lucide-react'; + +interface FloatingOrbProps { + size?: number; + color?: string; + delay?: number; + x?: number; + y?: number; + duration?: number; + icon?: any; + className?: string; +} + +function FloatingOrb({ + size = 80, + color = 'rgba(196, 30, 58, 0.08)', + delay = 0, + x = 0, + y = 0, + duration = 8, + icon: Icon, + className = '' +}: FloatingOrbProps) { + return ( + + {Icon && ( +
+ +
+ )} +
+ ); +} + +interface FloatingLineProps { + startX?: number; + startY?: number; + endX?: number; + endY?: number; + color?: string; + delay?: number; + duration?: number; + className?: string; +} + +function FloatingLine({ + startX = 0, + startY = 0, + endX = 200, + endY = 0, + color = 'rgba(28, 28, 28, 0.1)', + delay = 0, + duration = 6, + className = '' +}: FloatingLineProps) { + return ( + + + + ); +} + +interface FloatingIconProps { + icon: any; + size?: number; + color?: string; + delay?: number; + x?: number; + y?: number; + rotation?: number; + className?: string; +} + +function FloatingIcon({ + icon: Icon, + size = 24, + color = '#1C1C1C', + delay = 0, + x = 0, + y = 0, + rotation = 0, + className = '' +}: FloatingIconProps) { + return ( + +
+ +
+
+ ); +} + +interface ParticleRingProps { + size?: number; + color?: string; + delay?: number; + x?: number; + y?: number; + className?: string; +} + +function ParticleRing({ + size = 120, + color = 'rgba(196, 30, 58, 0.1)', + delay = 0, + x = 0, + y = 0, + className = '' +}: ParticleRingProps) { + return ( + + + {[0, 60, 120, 180, 240, 300].map((angle, i) => { + const rad = (angle * Math.PI) / 180; + const px = 60 + Math.cos(rad) * 45; + const py = 60 + Math.sin(rad) * 45; + return ( + + ); + })} + + + + ); +} + +interface GlowingDotProps { + size?: number; + color?: string; + delay?: number; + x?: number; + y?: number; + className?: string; +} + +function GlowingDot({ + size = 8, + color = '#C41E3A', + delay = 0, + x = 0, + y = 0, + className = '' +}: GlowingDotProps) { + return ( + + ); +} + +interface AdvancedFloatingEffectsProps { + variant?: 'minimal' | 'balanced' | 'rich' | 'parallax'; + className?: string; +} + +export function AdvancedFloatingEffects({ + variant = 'balanced', + className = '' +}: AdvancedFloatingEffectsProps) { + const [isMounted, setIsMounted] = useState(false); + const containerRef = useRef(null); + const { scrollY } = useScroll(); + + useEffect(() => { + setIsMounted(true); + }, []); + + const config = { + minimal: { orbs: 2, icons: 3, rings: 0, lines: 2, dots: 5 }, + balanced: { orbs: 3, icons: 5, rings: 1, lines: 4, dots: 8 }, + rich: { orbs: 5, icons: 8, rings: 2, lines: 6, dots: 12 }, + parallax: { orbs: 4, icons: 6, rings: 2, lines: 5, dots: 10 }, + }; + + const { orbs, icons, rings, lines, dots } = config[variant]; + + const iconsList = [Cpu, Shield, Zap, Globe, FileText, TrendingUp, BarChart3, Users]; + + const elements = useMemo(() => { + if (!isMounted) return []; + + const items = []; + const width = typeof window !== 'undefined' ? window.innerWidth : 1920; + const height = typeof window !== 'undefined' ? window.innerHeight : 1080; + + for (let i = 0; i < orbs; i++) { + items.push({ + type: 'orb', + id: `orb-${i}`, + props: { + size: 60 + Math.random() * 60, + color: i % 2 === 0 ? 'rgba(196, 30, 58, 0.08)' : 'rgba(28, 28, 28, 0.05)', + delay: i * 0.5, + x: width * 0.1 + (i * width * 0.35), + y: height * 0.15 + Math.random() * height * 0.5, + duration: 7 + Math.random() * 4, + icon: i % 3 === 0 ? iconsList[i % iconsList.length] : undefined, + }, + parallaxDepth: 0.1 + i * 0.1, + }); + } + + for (let i = 0; i < icons; i++) { + items.push({ + type: 'icon', + id: `icon-${i}`, + props: { + icon: iconsList[i % iconsList.length], + size: 20, + color: i % 2 === 0 ? '#C41E3A' : '#1C1C1C', + delay: i * 0.4, + x: width * 0.08 + (i * width * 0.12), + y: height * 0.1 + Math.random() * height * 0.65, + rotation: -15 + Math.random() * 30, + }, + parallaxDepth: 0.2 + i * 0.05, + }); + } + + for (let i = 0; i < rings; i++) { + items.push({ + type: 'ring', + id: `ring-${i}`, + props: { + size: 100 + Math.random() * 80, + color: i % 2 === 0 ? 'rgba(196, 30, 58, 0.1)' : 'rgba(28, 28, 28, 0.08)', + delay: i * 0.8, + x: width * 0.2 + (i * width * 0.4), + y: height * 0.2 + Math.random() * height * 0.4, + }, + parallaxDepth: 0.05 + i * 0.1, + }); + } + + for (let i = 0; i < lines; i++) { + items.push({ + type: 'line', + id: `line-${i}`, + props: { + startX: width * 0.05 + (i * width * 0.15), + startY: height * 0.1 + Math.random() * height * 0.7, + endX: width * 0.05 + (i * width * 0.15) + 80 + Math.random() * 120, + endY: height * 0.1 + Math.random() * height * 0.7, + color: i % 2 === 0 ? 'rgba(196, 30, 58, 0.15)' : 'rgba(28, 28, 28, 0.1)', + delay: i * 0.6, + duration: 5 + Math.random() * 3, + }, + parallaxDepth: 0.15 + i * 0.05, + }); + } + + for (let i = 0; i < dots; i++) { + items.push({ + type: 'dot', + id: `dot-${i}`, + props: { + size: 4 + Math.random() * 6, + color: i % 3 === 0 ? '#C41E3A' : i % 3 === 1 ? '#1C1C1C' : '#D4A574', + delay: i * 0.3, + x: Math.random() * width, + y: Math.random() * height, + }, + parallaxDepth: 0.25 + i * 0.02, + }); + } + + return items; + }, [orbs, icons, rings, lines, dots, isMounted, iconsList]); + + const getParallaxStyle = (depth: number) => { + if (variant !== 'parallax') return {}; + const y = useTransform(scrollY, [0, 500], [0, -depth * 100]); + return { y }; + }; + + return ( +
+ {elements.map((el) => { + const parallaxStyle = getParallaxStyle(el.parallaxDepth); + + return ( + + {el.type === 'orb' && } + {el.type === 'icon' && } + {el.type === 'ring' && } + {el.type === 'line' && } + {el.type === 'dot' && } + + ); + })} +
+ ); +} + +export default AdvancedFloatingEffects; diff --git a/src/components/effects/mouse-interactive-particles.tsx b/src/components/effects/mouse-interactive-particles.tsx new file mode 100644 index 0000000..36974d5 --- /dev/null +++ b/src/components/effects/mouse-interactive-particles.tsx @@ -0,0 +1,194 @@ +'use client'; + +import { useEffect, useRef, useState, useCallback } from 'react'; + +interface InteractiveParticle { + x: number; + y: number; + originX: number; + originY: number; + vx: number; + vy: number; + size: number; + opacity: number; + color: string; + life: number; +} + +interface MouseInteractiveParticlesProps { + particleCount?: number; + className?: string; + colorScheme?: 'red' | 'dark' | 'mixed'; + interactionRadius?: number; +} + +export function MouseInteractiveParticles({ + particleCount = 80, + className = '', + colorScheme = 'mixed', + interactionRadius = 150, +}: MouseInteractiveParticlesProps) { + const canvasRef = useRef(null); + const [isMounted, setIsMounted] = useState(false); + const mouseRef = useRef({ x: -1000, y: -1000, active: false }); + const particlesRef = useRef([]); + const animationRef = useRef(null); + + const getColors = useCallback(() => { + switch (colorScheme) { + case 'red': + return ['#C41E3A', '#E04A68', '#A01830']; + case 'dark': + return ['#1C1C1C', '#2D2D2D', '#3E3E3E']; + case 'mixed': + default: + return ['#C41E3A', '#1C1C1C', '#D4A574']; + } + }, [colorScheme]); + + useEffect(() => { + setIsMounted(true); + }, []); + + useEffect(() => { + if (!isMounted) return; + + const canvas = canvasRef.current; + if (!canvas) return; + + const ctx = canvas.getContext('2d'); + if (!ctx) return; + + let width = window.innerWidth; + let height = window.innerHeight; + + const resize = () => { + width = window.innerWidth; + height = window.innerHeight; + canvas.width = width; + canvas.height = height; + initParticles(); + }; + + const initParticles = () => { + const colors = getColors(); + particlesRef.current = []; + + for (let i = 0; i < particleCount; i++) { + const x = Math.random() * width; + const y = Math.random() * height; + + particlesRef.current.push({ + x, + y, + originX: x, + originY: y, + vx: (Math.random() - 0.5) * 0.5, + vy: (Math.random() - 0.5) * 0.5, + size: Math.random() * 3 + 1, + opacity: Math.random() * 0.5 + 0.2, + color: colors[Math.floor(Math.random() * colors.length)], + life: Math.random() * Math.PI * 2, + }); + } + }; + + const handleMouseMove = (e: MouseEvent) => { + mouseRef.current.x = e.clientX; + mouseRef.current.y = e.clientY; + mouseRef.current.active = true; + }; + + const handleMouseLeave = () => { + mouseRef.current.x = -1000; + mouseRef.current.y = -1000; + mouseRef.current.active = false; + }; + + const animate = () => { + ctx.clearRect(0, 0, width, height); + + particlesRef.current.forEach((particle, i) => { + particle.life += 0.02; + + if (mouseRef.current.active) { + const dx = mouseRef.current.x - particle.x; + const dy = mouseRef.current.y - particle.y; + const distance = Math.sqrt(dx * dx + dy * dy); + + if (distance < interactionRadius) { + const force = (interactionRadius - distance) / interactionRadius; + const angle = Math.atan2(dy, dx); + particle.vx -= Math.cos(angle) * force * 0.5; + particle.vy -= Math.sin(angle) * force * 0.5; + } + } + + const returnForce = 0.01; + particle.vx += (particle.originX - particle.x) * returnForce; + particle.vy += (particle.originY - particle.y) * returnForce; + + particle.vx *= 0.98; + particle.vy *= 0.98; + particle.x += particle.vx; + particle.y += particle.vy; + + particle.x += Math.sin(particle.life + i) * 0.1; + particle.y += Math.cos(particle.life * 0.8 + i) * 0.1; + + ctx.beginPath(); + ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2); + ctx.fillStyle = particle.color; + ctx.globalAlpha = particle.opacity; + ctx.fill(); + + particlesRef.current.forEach((otherParticle, j) => { + if (i === j) return; + const dx = particle.x - otherParticle.x; + const dy = particle.y - otherParticle.y; + const distance = Math.sqrt(dx * dx + dy * dy); + + if (distance < 100) { + ctx.beginPath(); + ctx.moveTo(particle.x, particle.y); + ctx.lineTo(otherParticle.x, otherParticle.y); + ctx.strokeStyle = particle.color; + ctx.globalAlpha = 0.05 * (1 - distance / 100); + ctx.stroke(); + } + }); + }); + + ctx.globalAlpha = 1; + animationRef.current = requestAnimationFrame(animate); + }; + + resize(); + initParticles(); + animate(); + + window.addEventListener('resize', resize); + window.addEventListener('mousemove', handleMouseMove); + window.addEventListener('mouseleave', handleMouseLeave); + + return () => { + window.removeEventListener('resize', resize); + window.removeEventListener('mousemove', handleMouseMove); + window.removeEventListener('mouseleave', handleMouseLeave); + if (animationRef.current) { + cancelAnimationFrame(animationRef.current); + } + }; + }, [isMounted, particleCount, getColors, interactionRadius]); + + if (!isMounted) return null; + + return ( + + ); +} + +export default MouseInteractiveParticles; diff --git a/src/components/sections/about-section.tsx b/src/components/sections/about-section.tsx index ade40c8..4f69bb2 100644 --- a/src/components/sections/about-section.tsx +++ b/src/components/sections/about-section.tsx @@ -53,9 +53,6 @@ export function AboutSection() { className="max-w-4xl mx-auto" >
- - 关于我们 -

关于 {COMPANY_INFO.shortName}

diff --git a/src/components/sections/contact-section.tsx b/src/components/sections/contact-section.tsx index a1c70c0..a8fe408 100644 --- a/src/components/sections/contact-section.tsx +++ b/src/components/sections/contact-section.tsx @@ -130,7 +130,7 @@ export function ContactSection() {
-
+

发送消息

{isSubmitted ? ( -
+
@@ -226,7 +226,7 @@ export function ContactSection() {

感谢您的留言,我们会尽快与您联系!

) : ( -
+
{isSubmitting ? ( diff --git a/src/components/sections/news-section.tsx b/src/components/sections/news-section.tsx index 8d92d1d..0497eff 100644 --- a/src/components/sections/news-section.tsx +++ b/src/components/sections/news-section.tsx @@ -20,9 +20,6 @@ export function NewsSection() { transition={{ duration: 0.6 }} className="text-center max-w-3xl mx-auto mb-16" > - - 新闻动态 -

最新资讯

diff --git a/src/components/sections/products-section.tsx b/src/components/sections/products-section.tsx index 5d954e4..0d8d366 100644 --- a/src/components/sections/products-section.tsx +++ b/src/components/sections/products-section.tsx @@ -24,9 +24,6 @@ export function ProductsSection() { transition={{ duration: 0.6 }} className="text-center max-w-3xl mx-auto mb-16" > - - 产品服务 -

我们的产品

diff --git a/src/components/sections/services-section.tsx b/src/components/sections/services-section.tsx index 51f913a..60ef0bc 100644 --- a/src/components/sections/services-section.tsx +++ b/src/components/sections/services-section.tsx @@ -31,9 +31,6 @@ export function ServicesSection() { transition={{ duration: 0.6 }} className="text-center max-w-3xl mx-auto mb-16" > - - 核心业务 -

我们的 核心服务