¶Ô±ÈÐÂÎļþ |
| | |
| | | "use client"; |
| | | |
| | | import { useEffect, useRef } from 'react'; |
| | | import { motion } from 'framer-motion'; |
| | | |
| | | interface Node { |
| | | x: number; |
| | | y: number; |
| | | label: string; |
| | | color: string; |
| | | icon?: string; |
| | | isAI?: boolean; |
| | | } |
| | | |
| | | export default function SupplyChainAIMindMap() { |
| | | const canvasRef = useRef<HTMLCanvasElement>(null); |
| | | |
| | | useEffect(() => { |
| | | const canvas = canvasRef.current; |
| | | if (!canvas) return; |
| | | |
| | | const ctx = canvas.getContext('2d'); |
| | | if (!ctx) return; |
| | | |
| | | const setCanvasSize = () => { |
| | | canvas.width = canvas.offsetWidth; |
| | | canvas.height = canvas.offsetHeight; |
| | | }; |
| | | |
| | | setCanvasSize(); |
| | | window.addEventListener('resize', setCanvasSize); |
| | | |
| | | // éåæ°æ®ç²åç³»ç» |
| | | interface QuantumParticle { |
| | | x: number; |
| | | y: number; |
| | | targetX: number; |
| | | targetY: number; |
| | | size: number; |
| | | color: string; |
| | | speed: number; |
| | | energy: number; |
| | | phase: number; |
| | | orbitRadius: number; |
| | | orbitSpeed: number; |
| | | pathIndex: number; |
| | | lifespan: number; |
| | | maxLifespan: number; |
| | | } |
| | | |
| | | interface Point { |
| | | x: number; |
| | | y: number; |
| | | } |
| | | |
| | | interface Path { |
| | | start: Point; |
| | | end: Point; |
| | | color: string; |
| | | particles: QuantumParticle[]; |
| | | } |
| | | |
| | | const particles: QuantumParticle[] = []; |
| | | |
| | | // å®ä¹æ°æ®æµè·¯å¾ |
| | | const paths: Path[] = [ |
| | | { start: { x: 0.15, y: 0.3 }, end: { x: 0.35, y: 0.3 }, color: '#6ADBFF', particles: [] }, // éè´å°ç产 |
| | | { start: { x: 0.35, y: 0.3 }, end: { x: 0.65, y: 0.3 }, color: '#5E72EB', particles: [] }, // ç产å°é
é |
| | | { start: { x: 0.65, y: 0.3 }, end: { x: 0.85, y: 0.3 }, color: '#FF6A88', particles: [] }, // é
éå°å®¢æ· |
| | | { start: { x: 0.85, y: 0.3 }, end: { x: 0.85, y: 0.6 }, color: '#FF9D6A', particles: [] }, // 客æ·å°éæ±é¢æµ |
| | | { start: { x: 0.85, y: 0.6 }, end: { x: 0.5, y: 0.5 }, color: '#FF6A88', particles: [] }, // 鿱颿µå°AI |
| | | { start: { x: 0.5, y: 0.5 }, end: { x: 0.15, y: 0.3 }, color: '#6ADBFF', particles: [] } // AIå°éè´ |
| | | ]; |
| | | |
| | | // å建éåç²å |
| | | const createQuantumParticle = () => { |
| | | const pathIndex = Math.floor(Math.random() * paths.length); |
| | | const path = paths[pathIndex]; |
| | | const startX = path.start.x * canvas.width; |
| | | const startY = path.start.y * canvas.height; |
| | | const endX = path.end.x * canvas.width; |
| | | const endY = path.end.y * canvas.height; |
| | | |
| | | const particle: QuantumParticle = { |
| | | x: startX, |
| | | y: startY, |
| | | targetX: endX, |
| | | targetY: endY, |
| | | size: Math.random() * 2 + 1, |
| | | color: path.color, |
| | | speed: 0.5 + Math.random() * 1, |
| | | energy: 1, |
| | | phase: Math.random() * Math.PI * 2, |
| | | orbitRadius: Math.random() * 20 + 10, |
| | | orbitSpeed: (Math.random() * 0.1 + 0.05) * (Math.random() < 0.5 ? 1 : -1), |
| | | pathIndex, |
| | | lifespan: 0, |
| | | maxLifespan: 150 + Math.random() * 100 |
| | | }; |
| | | |
| | | particles.push(particle); |
| | | }; |
| | | |
| | | // æ´æ°éåç²å |
| | | const updateParticles = () => { |
| | | const time = Date.now() * 0.001; |
| | | |
| | | for (let i = particles.length - 1; i >= 0; i--) { |
| | | const p = particles[i]; |
| | | p.lifespan++; |
| | | |
| | | if (p.lifespan >= p.maxLifespan) { |
| | | particles.splice(i, 1); |
| | | continue; |
| | | } |
| | | |
| | | // 计ç®å°ç®æ çæ¹ååè·ç¦» |
| | | const dx = p.targetX - p.x; |
| | | const dy = p.targetY - p.y; |
| | | const dist = Math.sqrt(dx * dx + dy * dy); |
| | | |
| | | if (dist < 5) { |
| | | // å°è¾¾ç®æ ç¹ï¼è®¾ç½®æ°ç®æ |
| | | const nextPathIndex = (p.pathIndex + 1) % paths.length; |
| | | const nextPath = paths[nextPathIndex]; |
| | | p.pathIndex = nextPathIndex; |
| | | p.targetX = nextPath.end.x * canvas.width; |
| | | p.targetY = nextPath.end.y * canvas.height; |
| | | p.phase = Math.random() * Math.PI * 2; // éç½®ç¸ä½ |
| | | } |
| | | |
| | | // éå轨éè¿å¨ |
| | | const progress = p.lifespan / p.maxLifespan; |
| | | const orbitAmplitude = Math.sin(progress * Math.PI) * p.orbitRadius; |
| | | const orbitPhase = p.phase + time * p.orbitSpeed; |
| | | |
| | | // 计ç®ä¸»è¦ç§»å¨æ¹å |
| | | const moveX = dx / dist * p.speed; |
| | | const moveY = dy / dist * p.speed; |
| | | |
| | | // æ·»å éå轨éåç§» |
| | | const perpX = -dy / dist; |
| | | const perpY = dx / dist; |
| | | |
| | | p.x += moveX + perpX * Math.sin(orbitPhase) * orbitAmplitude * 0.1; |
| | | p.y += moveY + perpY * Math.sin(orbitPhase) * orbitAmplitude * 0.1; |
| | | |
| | | // æ´æ°è½éå¼ |
| | | p.energy = Math.sin(progress * Math.PI); |
| | | } |
| | | }; |
| | | |
| | | // ç»å¶åºæ¯ |
| | | const drawScene = () => { |
| | | ctx.clearRect(0, 0, canvas.width, canvas.height); |
| | | |
| | | // ç»å¶ç§ææèæ¯ |
| | | drawTechBackground(); |
| | | |
| | | // ç»å¶è½éåºè·¯å¾ |
| | | drawEnergyFields(); |
| | | |
| | | // ç»å¶èç¹ |
| | | drawNodes(); |
| | | |
| | | // ç»å¶éåç²å |
| | | drawQuantumParticles(); |
| | | }; |
| | | |
| | | // ç»å¶ç§ææèæ¯ |
| | | const drawTechBackground = () => { |
| | | const gridSize = 40; |
| | | const time = Date.now() * 0.001; |
| | | |
| | | // ç»å¶å¨æç½æ ¼ |
| | | ctx.strokeStyle = 'rgba(70, 130, 180, 0.05)'; |
| | | ctx.lineWidth = 0.5; |
| | | |
| | | for (let y = 0; y < canvas.height; y += gridSize) { |
| | | ctx.beginPath(); |
| | | ctx.moveTo(0, y); |
| | | ctx.lineTo(canvas.width, y); |
| | | ctx.stroke(); |
| | | |
| | | // æ·»å æ³¢å¨ææ |
| | | ctx.beginPath(); |
| | | for (let x = 0; x < canvas.width; x += 5) { |
| | | const wave = Math.sin(x * 0.02 + time + y * 0.01) * 2; |
| | | ctx.lineTo(x, y + wave); |
| | | } |
| | | ctx.strokeStyle = 'rgba(70, 130, 180, 0.02)'; |
| | | ctx.stroke(); |
| | | } |
| | | |
| | | for (let x = 0; x < canvas.width; x += gridSize) { |
| | | ctx.beginPath(); |
| | | ctx.moveTo(x, 0); |
| | | ctx.lineTo(x, canvas.height); |
| | | ctx.strokeStyle = 'rgba(70, 130, 180, 0.05)'; |
| | | ctx.stroke(); |
| | | } |
| | | |
| | | // æ·»å éæºå
ç¹ |
| | | for (let i = 0; i < 15; i++) { |
| | | const x = Math.random() * canvas.width; |
| | | const y = Math.random() * canvas.height; |
| | | const size = 2 + Math.sin(time * 2 + i) * 1; |
| | | |
| | | const gradient = ctx.createRadialGradient(x, y, 0, x, y, size * 2); |
| | | gradient.addColorStop(0, 'rgba(106, 219, 255, 0.2)'); |
| | | gradient.addColorStop(1, 'rgba(106, 219, 255, 0)'); |
| | | |
| | | ctx.beginPath(); |
| | | ctx.fillStyle = gradient; |
| | | ctx.arc(x, y, size * 2, 0, Math.PI * 2); |
| | | ctx.fill(); |
| | | } |
| | | }; |
| | | |
| | | // ç»å¶è½éåº |
| | | const drawEnergyFields = () => { |
| | | const time = Date.now() * 0.001; |
| | | |
| | | paths.forEach((path: Path) => { |
| | | const startX = path.start.x * canvas.width; |
| | | const startY = path.start.y * canvas.height; |
| | | const endX = path.end.x * canvas.width; |
| | | const endY = path.end.y * canvas.height; |
| | | |
| | | // å建è½éåºææ |
| | | const numPoints = 30; |
| | | const fieldWidth = 40; |
| | | |
| | | // 计ç®è·¯å¾æ¹å |
| | | const dx = endX - startX; |
| | | const dy = endY - startY; |
| | | const length = Math.sqrt(dx * dx + dy * dy); |
| | | const dirX = dx / length; |
| | | const dirY = dy / length; |
| | | |
| | | // åç´æ¹å |
| | | const perpX = -dirY; |
| | | const perpY = dirX; |
| | | |
| | | // ç»å¶è½éæµå¨ææ |
| | | for (let i = 0; i < numPoints; i++) { |
| | | const progress = i / (numPoints - 1); |
| | | const x = startX + dx * progress; |
| | | const y = startY + dy * progress; |
| | | |
| | | // åå»ºæ³¢æµªææ |
| | | const waveAmplitude = Math.sin(progress * Math.PI * 4 + time * 3) * fieldWidth; |
| | | const waveFrequency = Math.cos(progress * Math.PI * 2 - time * 2) * fieldWidth * 0.3; |
| | | |
| | | const x1 = x + perpX * waveAmplitude + dirX * waveFrequency; |
| | | const y1 = y + perpY * waveAmplitude + dirY * waveFrequency; |
| | | const x2 = x - perpX * waveAmplitude - dirX * waveFrequency; |
| | | const y2 = y - perpY * waveAmplitude - dirY * waveFrequency; |
| | | |
| | | const gradient = ctx.createLinearGradient(x1, y1, x2, y2); |
| | | const alpha = (Math.sin(progress * Math.PI + time) + 1) * 0.3; |
| | | const alphaHex = Math.floor(alpha * 255).toString(16).padStart(2, '0'); |
| | | |
| | | gradient.addColorStop(0, `${path.color}00`); |
| | | gradient.addColorStop(0.5, `${path.color}${alphaHex}`); |
| | | gradient.addColorStop(1, `${path.color}00`); |
| | | |
| | | ctx.beginPath(); |
| | | ctx.strokeStyle = gradient; |
| | | ctx.lineWidth = 2; |
| | | ctx.moveTo(x1, y1); |
| | | ctx.lineTo(x2, y2); |
| | | ctx.stroke(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // ç»å¶èç¹ |
| | | const drawNodes = () => { |
| | | // èç¹é
ç½® |
| | | const nodes: Node[] = [ |
| | | { x: 0.15, y: 0.3, label: 'éè´', color: '#6ADBFF', icon: 'ð¦' }, |
| | | { x: 0.35, y: 0.3, label: 'ç产', color: '#5E72EB', icon: 'ð' }, |
| | | { x: 0.65, y: 0.3, label: 'é
é', color: '#FF6A88', icon: 'ð' }, |
| | | { x: 0.85, y: 0.3, label: '客æ·', color: '#FF9D6A', icon: 'ð¥' }, |
| | | { x: 0.85, y: 0.6, label: '鿱颿µ', color: '#FF6A88', icon: 'ð' }, |
| | | { x: 0.5, y: 0.5, label: 'æºè½åæå¼æ', color: '#5E72EB', isAI: true }, |
| | | ]; |
| | | |
| | | const time = Date.now() * 0.001; |
| | | |
| | | nodes.forEach(node => { |
| | | const x = node.x * canvas.width; |
| | | const y = node.y * canvas.height; |
| | | |
| | | if (node.isAI) { |
| | | drawAINode(x, y); |
| | | } else { |
| | | // èç¹å
ç¯ææ |
| | | const pulseSize = 24 + Math.sin(time * 1.5 + x * 0.05) * 3; |
| | | |
| | | // å¤åå
|
| | | const gradient = ctx.createRadialGradient(x, y, 15, x, y, pulseSize); |
| | | gradient.addColorStop(0, `${node.color}30`); |
| | | gradient.addColorStop(1, `${node.color}00`); |
| | | |
| | | ctx.beginPath(); |
| | | ctx.arc(x, y, pulseSize, 0, Math.PI * 2); |
| | | ctx.fillStyle = gradient; |
| | | ctx.fill(); |
| | | |
| | | // èç¹ä¸»ä½ |
| | | ctx.beginPath(); |
| | | ctx.arc(x, y, 20, 0, Math.PI * 2); |
| | | ctx.fillStyle = `${node.color}20`; |
| | | ctx.fill(); |
| | | |
| | | ctx.beginPath(); |
| | | ctx.arc(x, y, 20, 0, Math.PI * 2); |
| | | ctx.strokeStyle = node.color; |
| | | ctx.lineWidth = 2; |
| | | ctx.stroke(); |
| | | |
| | | // 徿 |
| | | if (node.icon) { |
| | | ctx.fillStyle = node.color; |
| | | ctx.font = '16px monospace'; |
| | | ctx.textAlign = 'center'; |
| | | ctx.textBaseline = 'middle'; |
| | | ctx.fillText(node.icon, x, y - 3); |
| | | } |
| | | |
| | | // æ ç¾ |
| | | ctx.fillStyle = '#fff'; |
| | | ctx.font = '12px sans-serif'; |
| | | ctx.fillText(node.label, x, y + 25); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // ç»å¶AIèç¹ |
| | | const drawAINode = (x: number, y: number) => { |
| | | const time = Date.now() * 0.001; |
| | | |
| | | // ç¥ç»ç½ç»èæ¯ææ |
| | | const neuronPoints = [ |
| | | { x: -20, y: -15 }, { x: 20, y: -15 }, |
| | | { x: -25, y: 0 }, { x: 25, y: 0 }, |
| | | { x: -20, y: 15 }, { x: 20, y: 15 }, |
| | | { x: 0, y: -20 }, { x: 0, y: 20 }, |
| | | { x: -15, y: -25 }, { x: 15, y: -25 }, |
| | | { x: -15, y: 25 }, { x: 15, y: 25 } |
| | | ]; |
| | | |
| | | // ç»å¶ç¥ç»è¿æ¥ |
| | | ctx.strokeStyle = 'rgba(106, 219, 255, 0.3)'; |
| | | ctx.lineWidth = 1; |
| | | |
| | | neuronPoints.forEach((point, i) => { |
| | | for (let j = i + 1; j < neuronPoints.length; j++) { |
| | | const p1 = { x: x + point.x, y: y + point.y }; |
| | | const p2 = { x: x + neuronPoints[j].x, y: y + neuronPoints[j].y }; |
| | | |
| | | const pulsePhase = (time * 2 + i * 0.5 + j * 0.3) % (Math.PI * 2); |
| | | const pulseIntensity = (Math.sin(pulsePhase) + 1) * 0.5; |
| | | |
| | | const gradient = ctx.createLinearGradient(p1.x, p1.y, p2.x, p2.y); |
| | | gradient.addColorStop(0, `rgba(106, 219, 255, ${0.1 + pulseIntensity * 0.2})`); |
| | | gradient.addColorStop(0.5, `rgba(94, 114, 235, ${0.2 + pulseIntensity * 0.3})`); |
| | | gradient.addColorStop(1, `rgba(106, 219, 255, ${0.1 + pulseIntensity * 0.2})`); |
| | | |
| | | ctx.beginPath(); |
| | | ctx.strokeStyle = gradient; |
| | | ctx.moveTo(p1.x, p1.y); |
| | | |
| | | // åå»ºå¨æå¼§å½¢è¿æ¥ |
| | | const controlPoint = { |
| | | x: (p1.x + p2.x) / 2 + Math.sin(time + i) * 10, |
| | | y: (p1.y + p2.y) / 2 + Math.cos(time + j) * 10 |
| | | }; |
| | | |
| | | ctx.quadraticCurveTo(controlPoint.x, controlPoint.y, p2.x, p2.y); |
| | | ctx.stroke(); |
| | | |
| | | // å¨è¿æ¥çº¿ä¸æ·»å æµå¨ç²å |
| | | const particlePos = Math.sin(time * 2 + i * 0.7 + j * 0.5) * 0.5 + 0.5; |
| | | const px = p1.x + (p2.x - p1.x) * particlePos; |
| | | const py = p1.y + (p2.y - p1.y) * particlePos; |
| | | |
| | | const particleGlow = ctx.createRadialGradient(px, py, 0, px, py, 3); |
| | | particleGlow.addColorStop(0, 'rgba(255, 255, 255, 0.8)'); |
| | | particleGlow.addColorStop(1, 'rgba(106, 219, 255, 0)'); |
| | | |
| | | ctx.fillStyle = particleGlow; |
| | | ctx.beginPath(); |
| | | ctx.arc(px, py, 3, 0, Math.PI * 2); |
| | | ctx.fill(); |
| | | } |
| | | }); |
| | | |
| | | // ç»å¶å¤§èè½®å» |
| | | ctx.beginPath(); |
| | | ctx.moveTo(x - 25, y + 20); |
| | | |
| | | // å·¦å边大è |
| | | const leftOffset = Math.sin(time * 2) * 2; |
| | | ctx.bezierCurveTo( |
| | | x - 40 + leftOffset, y + 10, // æ§å¶ç¹1 |
| | | x - 40 + leftOffset, y - 20, // æ§å¶ç¹2 |
| | | x - 25, y - 25 // ç»ç¹ |
| | | ); |
| | | |
| | | // ä¸é¨è¤¶ç± |
| | | const topOffset = Math.sin(time * 2.5) * 2; |
| | | ctx.bezierCurveTo( |
| | | x - 20, y - 30 + topOffset, |
| | | x - 10, y - 35 + topOffset, |
| | | x, y - 25 |
| | | ); |
| | | |
| | | // å³å边大è |
| | | const rightOffset = Math.sin(time * 2 + Math.PI) * 2; |
| | | ctx.bezierCurveTo( |
| | | x + 10, y - 35 + topOffset, |
| | | x + 20, y - 30 + topOffset, |
| | | x + 25, y - 25 |
| | | ); |
| | | |
| | | ctx.bezierCurveTo( |
| | | x + 40 + rightOffset, y - 20, |
| | | x + 40 + rightOffset, y + 10, |
| | | x + 25, y + 20 |
| | | ); |
| | | |
| | | // åºé¨æ²çº¿ |
| | | const bottomOffset = Math.sin(time * 1.5) * 2; |
| | | ctx.bezierCurveTo( |
| | | x + 15, y + 25 + bottomOffset, |
| | | x - 15, y + 25 + bottomOffset, |
| | | x - 25, y + 20 |
| | | ); |
| | | |
| | | // åå»ºå¤§èæ¸å |
| | | const brainGradient = ctx.createRadialGradient(x, y, 0, x, y, 40); |
| | | brainGradient.addColorStop(0, 'rgba(94, 114, 235, 0.8)'); |
| | | brainGradient.addColorStop(0.5, 'rgba(106, 219, 255, 0.6)'); |
| | | brainGradient.addColorStop(1, 'rgba(94, 114, 235, 0.4)'); |
| | | |
| | | ctx.fillStyle = brainGradient; |
| | | ctx.fill(); |
| | | |
| | | // æ·»å èæ³¢å¨ç»ææ |
| | | for (let i = 0; i < 3; i++) { |
| | | const waveRadius = 35 + Math.sin(time * 2 + i * Math.PI / 3) * 5; |
| | | const waveGradient = ctx.createRadialGradient(x, y, waveRadius - 5, x, y, waveRadius + 5); |
| | | waveGradient.addColorStop(0, 'rgba(106, 219, 255, 0)'); |
| | | waveGradient.addColorStop(0.5, `rgba(94, 114, 235, ${0.1 - i * 0.03})`); |
| | | waveGradient.addColorStop(1, 'rgba(106, 219, 255, 0)'); |
| | | |
| | | ctx.beginPath(); |
| | | ctx.arc(x, y, waveRadius, 0, Math.PI * 2); |
| | | ctx.fillStyle = waveGradient; |
| | | ctx.fill(); |
| | | } |
| | | |
| | | // æ·»å ç¥ç»å
éªçææ |
| | | neuronPoints.forEach((point, i) => { |
| | | const neuronX = x + point.x; |
| | | const neuronY = y + point.y; |
| | | const pulseScale = Math.sin(time * 3 + i) * 0.5 + 1.5; |
| | | |
| | | const neuronGlow = ctx.createRadialGradient( |
| | | neuronX, neuronY, 0, |
| | | neuronX, neuronY, 4 * pulseScale |
| | | ); |
| | | neuronGlow.addColorStop(0, 'rgba(255, 255, 255, 0.8)'); |
| | | neuronGlow.addColorStop(0.5, 'rgba(106, 219, 255, 0.4)'); |
| | | neuronGlow.addColorStop(1, 'rgba(94, 114, 235, 0)'); |
| | | |
| | | ctx.beginPath(); |
| | | ctx.arc(neuronX, neuronY, 4 * pulseScale, 0, Math.PI * 2); |
| | | ctx.fillStyle = neuronGlow; |
| | | ctx.fill(); |
| | | }); |
| | | |
| | | // æ·»å èæ²çº¹ç |
| | | const numFolds = 8; |
| | | for (let i = 0; i < numFolds; i++) { |
| | | const angle = (Math.PI * 2 / numFolds) * i; |
| | | const radius = 25; |
| | | const x1 = x + Math.cos(angle) * radius; |
| | | const y1 = y + Math.sin(angle) * radius; |
| | | const x2 = x + Math.cos(angle + Math.PI / numFolds) * (radius * 0.7); |
| | | const y2 = y + Math.sin(angle + Math.PI / numFolds) * (radius * 0.7); |
| | | |
| | | ctx.beginPath(); |
| | | ctx.moveTo(x1, y1); |
| | | ctx.lineTo(x2, y2); |
| | | ctx.strokeStyle = 'rgba(94, 114, 235, 0.3)'; |
| | | ctx.lineWidth = 1; |
| | | ctx.stroke(); |
| | | } |
| | | |
| | | // æ ç¾ |
| | | ctx.fillStyle = '#FFFFFF'; |
| | | ctx.font = '13px sans-serif'; |
| | | ctx.textAlign = 'center'; |
| | | ctx.textBaseline = 'middle'; |
| | | ctx.fillText('æºè½åæå¼æ', x, y + 45); |
| | | }; |
| | | |
| | | // ç»å¶éåç²å |
| | | const drawQuantumParticles = () => { |
| | | particles.forEach(p => { |
| | | const time = Date.now() * 0.001; |
| | | |
| | | // éåå
æææ |
| | | const glowSize = p.size * (2 + Math.sin(time * 3 + p.phase) * 0.5); |
| | | const gradient = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, glowSize * 2); |
| | | gradient.addColorStop(0, p.color + 'FF'); |
| | | gradient.addColorStop(0.5, p.color + '40'); |
| | | gradient.addColorStop(1, p.color + '00'); |
| | | |
| | | ctx.beginPath(); |
| | | ctx.arc(p.x, p.y, glowSize, 0, Math.PI * 2); |
| | | ctx.fillStyle = gradient; |
| | | ctx.fill(); |
| | | |
| | | // éåæ ¸å¿ |
| | | ctx.beginPath(); |
| | | ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2); |
| | | ctx.fillStyle = '#FFFFFF'; |
| | | ctx.fill(); |
| | | |
| | | // éåæ³¢çº¹ |
| | | if (Math.random() < 0.1) { |
| | | const waveGradient = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.size * 4); |
| | | waveGradient.addColorStop(0, p.color + '40'); |
| | | waveGradient.addColorStop(1, p.color + '00'); |
| | | |
| | | ctx.beginPath(); |
| | | ctx.arc(p.x, p.y, p.size * 4, 0, Math.PI * 2); |
| | | ctx.fillStyle = waveGradient; |
| | | ctx.fill(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // åå§åç²åç³»ç» |
| | | const initParticles = () => { |
| | | for (let i = 0; i < 40; i++) { |
| | | setTimeout(() => createQuantumParticle(), i * 100); |
| | | } |
| | | }; |
| | | |
| | | // å®ææ·»å æ°ç²å |
| | | const particleInterval = setInterval(() => { |
| | | if (particles.length < 50) { |
| | | createQuantumParticle(); |
| | | } |
| | | }, 500); |
| | | |
| | | // å¨ç»å¾ªç¯ |
| | | const animate = () => { |
| | | updateParticles(); |
| | | drawScene(); |
| | | requestAnimationFrame(animate); |
| | | }; |
| | | |
| | | initParticles(); |
| | | animate(); |
| | | |
| | | return () => { |
| | | window.removeEventListener('resize', setCanvasSize); |
| | | clearInterval(particleInterval); |
| | | }; |
| | | }, []); |
| | | |
| | | return ( |
| | | <div className="w-full py-4"> |
| | | <motion.div |
| | | initial={{ opacity: 0, y: 20 }} |
| | | whileInView={{ opacity: 1, y: 0 }} |
| | | viewport={{ once: false, margin: "-100px" }} |
| | | transition={{ duration: 0.7, ease: "easeOut" }} |
| | | className="text-center mb-8" |
| | | > |
| | | <h2 className="text-3xl md:text-4xl font-bold text-white mb-5"> |
| | | <span className="text-transparent bg-clip-text bg-gradient-to-r from-[#6ADBFF] to-[#5E72EB]"> |
| | | AIèµè½APS |
| | | </span> ä¼å¿å
¨æ¯ |
| | | </h2> |
| | | <p className="text-gray-300 max-w-3xl mx-auto"> |
| | | å
¨å±æºè½ååï¼ä»ä¾åºé¾åç¯èæ¶éæ°æ®ï¼åºäºæ·±åº¦å¦ä¹ ä¸éåå¯åç®æ³å®ç°ç«¯å°ç«¯çæºè½å³çä¸ä¼å |
| | | </p> |
| | | </motion.div> |
| | | |
| | | <motion.div |
| | | initial={{ opacity: 0 }} |
| | | whileInView={{ opacity: 1 }} |
| | | viewport={{ once: false, margin: "-100px" }} |
| | | transition={{ duration: 1, ease: "easeOut" }} |
| | | className="relative h-[450px] w-full backdrop-blur-sm bg-white/5 rounded-xl border border-[#6ADBFF]/20 overflow-hidden" |
| | | > |
| | | <canvas |
| | | ref={canvasRef} |
| | | className="absolute inset-0 w-full h-full" |
| | | /> |
| | | |
| | | <div className="absolute bottom-0 left-0 right-0 flex justify-center gap-6 p-6 bg-gradient-to-t from-[#0A1033]/90 to-transparent"> |
| | | <motion.div |
| | | initial={{ opacity: 0, y: 20 }} |
| | | whileInView={{ opacity: 1, y: 0 }} |
| | | viewport={{ once: false }} |
| | | transition={{ duration: 0.7, delay: 0.2 }} |
| | | className="backdrop-blur-sm bg-white/5 rounded-lg p-4 border border-[#6ADBFF]/20 w-[30%]" |
| | | > |
| | | <h3 className="text-lg font-semibold text-[#6ADBFF] mb-2">卿ååº</h3> |
| | | <p className="text-gray-300 text-sm">宿¶æç¥ä¾åºé¾ååï¼æºè½è°æ´ç产计åï¼æå40%ååºé度</p> |
| | | </motion.div> |
| | | |
| | | <motion.div |
| | | initial={{ opacity: 0, y: 20 }} |
| | | whileInView={{ opacity: 1, y: 0 }} |
| | | viewport={{ once: false }} |
| | | transition={{ duration: 0.7, delay: 0.3 }} |
| | | className="backdrop-blur-sm bg-white/5 rounded-lg p-4 border border-[#5E72EB]/20 w-[30%]" |
| | | > |
| | | <h3 className="text-lg font-semibold text-[#5E72EB] mb-2">颿µä¼å</h3> |
| | | <p className="text-gray-300 text-sm">åºäºæ·±åº¦å¦ä¹ 颿µå¸åºéæ±ï¼éä½25%åºåææ¬ï¼æé«ç©æå©ç¨ç</p> |
| | | </motion.div> |
| | | |
| | | <motion.div |
| | | initial={{ opacity: 0, y: 20 }} |
| | | whileInView={{ opacity: 1, y: 0 }} |
| | | viewport={{ once: false }} |
| | | transition={{ duration: 0.7, delay: 0.4 }} |
| | | className="backdrop-blur-sm bg-white/5 rounded-lg p-4 border border-[#FF6A88]/20 w-[30%]" |
| | | > |
| | | <h3 className="text-lg font-semibold text-[#FF6A88] mb-2">å¤ç»´å³ç</h3> |
| | | <p className="text-gray-300 text-sm">å¹³è¡¡ææ¬ã交æãè´¨éå¤ç»´ç®æ ï¼å®ç°çæ£å
¨å±æä¼è§£</p> |
| | | </motion.div> |
| | | </div> |
| | | </motion.div> |
| | | </div> |
| | | ); |
| | | } |