// Main JS file for blog interactivity document.addEventListener('DOMContentLoaded', () => { // Show toast message from URL parameter if present const urlParams = new URLSearchParams(window.location.search); const msg = urlParams.get('msg'); if (msg) { showToast(msg); } }); function showToast(message) { const toast = document.createElement('div'); toast.className = 'toast'; toast.textContent = message; toast.style.display = 'block'; document.body.appendChild(toast); setTimeout(() => { toast.style.opacity = '0'; toast.style.transition = 'opacity 0.5s'; setTimeout(() => toast.remove(), 500); }, 3000); } // --- Interactive Background --- document.addEventListener('DOMContentLoaded', () => { initInteractiveBackground(); }); function initInteractiveBackground() { // 1. Mouse Glow & Parallax shapes const mouseShape = document.querySelector('.shape-mouse'); const bgShapes = document.querySelectorAll('.bg-shapes .shape:not(.shape-mouse)'); let mouseX = window.innerWidth / 2; let mouseY = window.innerHeight / 2; let currentX = mouseX; let currentY = mouseY; // Parallax variables const windowCenterX = window.innerWidth / 2; const windowCenterY = window.innerHeight / 2; document.addEventListener('mousemove', (e) => { mouseX = e.clientX; mouseY = e.clientY; // Make the mouse glow visible once mouse moves if (mouseShape && mouseShape.style.opacity === '0') { mouseShape.style.opacity = '1'; } // Simple parallax for the background blobs bgShapes.forEach(shape => { const speed = parseFloat(shape.getAttribute('data-speed') || 1); const x = (windowCenterX - mouseX) * speed * 0.05; const y = (windowCenterY - mouseY) * speed * 0.05; // Use translation combined with the existing animation (in CSS, we'll just layer it) // Note: Since css animation overrides transform, we can apply parallax translation // by updating margin or a nested div. Alternatively, since shape-1 and shape-2 // have keyframes using transform, we shouldn't overwrite transform directly. // Let's use margin instead for a simple offset! shape.style.marginLeft = `${x}px`; shape.style.marginTop = `${y}px`; }); }); // Smooth follow for the glowing orb function animateGlow() { if (mouseShape) { currentX += (mouseX - currentX) * 0.1; currentY += (mouseY - currentY) * 0.1; // Center the 300x300 shape on the cursor (so subtract half width/height) mouseShape.style.transform = `translate(${currentX}px, ${currentY}px)`; } requestAnimationFrame(animateGlow); } animateGlow(); // 2. Interactive Canvas Particles const canvas = document.getElementById('bg-canvas'); if (!canvas) return; const ctx = canvas.getContext('2d'); let particles = []; function resizeCanvas() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; initParticles(); } class Particle { constructor() { this.x = Math.random() * canvas.width; this.y = Math.random() * canvas.height; this.size = Math.random() * 2 + 0.5; this.baseX = this.x; this.baseY = this.y; this.density = (Math.random() * 30) + 1; this.speedX = (Math.random() - 0.5) * 0.5; this.speedY = (Math.random() - 0.5) * 0.5; } update() { this.x += this.speedX; this.y += this.speedY; // Bounce off edges if (this.x > canvas.width || this.x < 0) this.speedX = -this.speedX; if (this.y > canvas.height || this.y < 0) this.speedY = -this.speedY; // Mouse interaction (repel) let dx = mouseX - this.x; let dy = mouseY - this.y; let distance = Math.sqrt(dx * dx + dy * dy); let forceDirectionX = dx / distance; let forceDirectionY = dy / distance; let maxDistance = 150; let force = (maxDistance - distance) / maxDistance; let directionX = forceDirectionX * force * this.density; let directionY = forceDirectionY * force * this.density; if (distance < maxDistance) { this.x -= directionX; this.y -= directionY; } } draw() { ctx.fillStyle = 'rgba(37, 99, 235, 0.4)'; ctx.beginPath(); ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); ctx.closePath(); ctx.fill(); } } function initParticles() { particles = []; let numParticles = Math.min((canvas.width * canvas.height) / 10000, 100); // cap at 100 for perf for (let i = 0; i < numParticles; i++) { particles.push(new Particle()); } } function animateParticles() { ctx.clearRect(0, 0, canvas.width, canvas.height); for (let i = 0; i < particles.length; i++) { particles[i].update(); particles[i].draw(); // Connect particles for (let j = i; j < particles.length; j++) { let dx = particles[i].x - particles[j].x; let dy = particles[i].y - particles[j].y; let distance = Math.sqrt(dx * dx + dy * dy); if (distance < 120) { ctx.beginPath(); ctx.strokeStyle = `rgba(37, 99, 235, ${0.1 * (1 - distance/120)})`; ctx.lineWidth = 1; ctx.moveTo(particles[i].x, particles[i].y); ctx.lineTo(particles[j].x, particles[j].y); ctx.stroke(); ctx.closePath(); } } } requestAnimationFrame(animateParticles); } window.addEventListener('resize', resizeCanvas); resizeCanvas(); animateParticles(); }