let canvas, gravityCtx; const windParticles = []; const windParticleCount = 600; const windSpeed = 0.3; let gravityPoints = []; let animationId = null; const frameInterval = 1000 / 50; let gravityRadius = 90; let particleStrokeWidth = 2; let randomnessFactor = 1.8; const initialOpacity = 0.15; const canvasWidth = 490; const canvasHeight = 490; // Inicializa as partículas de vento function initializeWindParticles() { windParticles.length = 0; if (gravityPoints.length === 0) { return false; } for (let i = 0; i < windParticleCount; i++) { const pointIndex = Math.floor(Math.random() * gravityPoints.length); const targetPoint = gravityPoints[pointIndex]; let startX, startY, distanceToCenter; let attempts = 0; do { startX = Math.random() * canvasWidth; startY = Math.random() * canvasHeight; const dx = targetPoint.x - startX; const dy = targetPoint.y - startY; distanceToCenter = Math.sqrt(dx * dx + dy * dy); attempts++; if (attempts > 100) break; } while (distanceToCenter < gravityRadius); const angle = Math.atan2(targetPoint.y - startY, targetPoint.x - startX); windParticles.push({ x: startX, y: startY, speed: windSpeed + Math.random(), direction: angle, life: Math.random() * 100, trail: [], opacity: initialOpacity }); } return true; } // Reinicia a partícula function reiniciarParticula(p) { if (gravityPoints.length === 0) return; const pointIndex = Math.floor(Math.random() * gravityPoints.length); const targetPoint = gravityPoints[pointIndex]; p.x = Math.random() * canvas.width; p.y = Math.random() * canvas.height; p.life = Math.random() * 100; p.speed = windSpeed + Math.random(); p.direction = Math.atan2(targetPoint.y - p.y, targetPoint.x - p.x); p.trail = []; p.opacity = initialOpacity; } // Aplica a gravidade e altera a trajetória function applyGravity(particle) { let totalForceX = 0; let totalForceY = 0; let closestPoint = null; let minDistance = Infinity; gravityPoints.forEach(point => { const dx = point.x - particle.x; const dy = point.y - particle.y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < minDistance) { minDistance = distance; closestPoint = point; } if (distance < gravityRadius * 5) { const force = point.strength / Math.pow(distance, 1.1); const angle = Math.atan2(dy, dx); totalForceX += Math.cos(angle) * force; totalForceY += Math.sin(angle) * force; if (distance < 10) { reiniciarParticula(particle); } } }); if (totalForceX || totalForceY) { const targetAngle = Math.atan2(totalForceY, totalForceX); if (Math.cos(targetAngle - particle.direction) < 0) { particle.direction = targetAngle; } else { particle.direction += (targetAngle - particle.direction) * randomnessFactor; } } particle.speed += Math.sqrt(totalForceX ** 2 + totalForceY ** 2) * 0.1; particle.speed = Math.min(particle.speed, windSpeed + 5); } // Desenha as partículas de vento function drawWindParticles() { if (!canvas || !document.contains(canvas)) { windAnimationActive = false; return; } if (!gravityCtx) { windAnimationActive = false; return; } gravityCtx.clearRect(0, 0, canvas.width, canvas.height); windParticles.forEach(p => { applyGravity(p); p.trail.push({ x: p.x, y: p.y }); if (p.trail.length > 20) { p.trail.shift(); } for (let i = 0; i < p.trail.length - 1; i++) { const opacity = (i + 1) / p.trail.length * p.opacity; gravityCtx.beginPath(); gravityCtx.moveTo(p.trail[i].x, p.trail[i].y); gravityCtx.lineTo(p.trail[i + 1].x, p.trail[i + 1].y); gravityCtx.strokeStyle = `rgba(255, 255, 255, ${opacity})`; gravityCtx.lineWidth = particleStrokeWidth; gravityCtx.stroke(); } p.x += Math.cos(p.direction) * p.speed; p.y += Math.sin(p.direction) * p.speed; if (p.x < 0 || p.x > canvas.width || p.y < 0 || p.y > canvas.height || p.life <= 0) { reiniciarParticula(p); } p.life -= 0.001; }); } // Inicia a animação do vento function startWindAnimation() { extractGravityPoints(); if (gravityPoints.length === 0) { windAnimationActive = false; return; } const matrixElement = document.getElementById('matrix'); if (!matrixElement) { windAnimationActive = false; return; } // Verifica se o canvas antigo ainda existe no DOM if (canvas && !document.contains(canvas)) { canvas = null; gravityCtx = null; } if (!canvas) { canvas = document.createElement('canvas'); canvas.width = canvasWidth; canvas.height = canvasHeight; canvas.style.position = "absolute"; canvas.style.top = "43%"; canvas.style.left = "50%"; canvas.style.transform = "translate(-50%, -50%)"; canvas.style.zIndex = "-1"; canvas.id = "windCanvas"; matrixElement.appendChild(canvas); gravityCtx = canvas.getContext('2d'); } if (!initializeWindParticles()) { windAnimationActive = false; return; } function animate() { if (!windAnimationActive) return; drawWindParticles(); animationId = requestAnimationFrame(animate); } animate(); } // Alterna a animação do vento function toggleWindAnimation() { windAnimationActive = !windAnimationActive; if (windAnimationActive) { if (animationId) { cancelAnimationFrame(animationId); animationId = null; } startWindAnimation(); } else { if (animationId) { cancelAnimationFrame(animationId); animationId = null; } if (gravityCtx && canvas) { gravityCtx.clearRect(0, 0, canvas.width, canvas.height); } } } // Função para extrair os pontos gravitacionais do campo oculto function extractGravityPoints() { const hiddenField = document.getElementById('hiddenCoordinates'); if (!hiddenField) return; const lines = hiddenField.value.split('\n').filter(line => line.trim() !== ''); gravityPoints = []; lines.forEach(line => { if (line.startsWith("(PG-")) { const strengthMatch = line.match(/\(PG-(\d+)\)/); const coordsMatch = line.match(/\)\s*([\d.]+),([\d.]+),([\d.]+),([\d.]+)/); if (strengthMatch && coordsMatch) { const strength = parseFloat(strengthMatch[1]); const coords = coordsMatch.slice(1).map(Number); if (coords.length === 4 && !coords.some(isNaN)) { const convertedCoords = convertCoordinates(coords); gravityPoints.push({ x: convertedCoords[0], y: convertedCoords[1], strength: strength }); } } } }); } // Função de conversão de coordenadas function convertCoordinates(coords) { const [a, b, c, d] = coords; const x = canvasWidth / 2 + (c - d) / 100 * canvasWidth / 2; const y = canvasHeight / 2 - (a - b) / 100 * canvasHeight / 2; return [x, y]; } // Observador para detectar mudanças no DOM que removam o canvas const observer = new MutationObserver(function(mutations) { if (canvas && !document.contains(canvas)) { if (windAnimationActive) { windAnimationActive = false; if (animationId) { cancelAnimationFrame(animationId); animationId = null; } } canvas = null; gravityCtx = null; } }); // Evento para iniciar a animação document.addEventListener('DOMContentLoaded', function () { const gravityButton = document.getElementById('gravityButton'); if (gravityButton) { gravityButton.addEventListener('click', toggleWindAnimation); } const matrixElement = document.getElementById('matrix'); if (matrixElement) { observer.observe(matrixElement, { childList: true, subtree: true }); } });