interface Props { // Color of the first set of particles (default: Ultramarine) primaryColor?: string; // Color of the second set of particles (default: Aqua) secondaryColor?: string; // Radius of mouse interaction (default: 150px) mouseRadius?: number; // Background color or gradient (default: Dark gradient) backgroundColor?: string; } export default function ParticleBackground({ primaryColor = "rgba(68, 116, 255, 0.8)", // Ultramarine secondaryColor = "rgba(66, 249, 255, 0.8)", // Aqua mouseRadius = 150, backgroundColor = "linear-gradient(to bottom, rgba(0,0,0,0.95), rgba(0,0,0,1))" }: Props) { return { canvas: document.createElement("canvas"), mount(canvas: HTMLCanvasElement) { // Set up canvas styles canvas.style.position = "fixed"; canvas.style.top = "0"; canvas.style.left = "0"; canvas.style.width = "100%"; canvas.style.height = "100%"; canvas.style.zIndex = "-1"; canvas.style.background = backgroundColor; const ctx = canvas.getContext("2d"); if (!ctx) return; // Initialize variables let particles: Array<{ x: number; y: number; vx: number; vy: number; targetX: number; targetY: number; radius: number; color: string; }> = []; const mouse = { x: 0, y: 0, radius: mouseRadius }; let animationFrame: number; // Update canvas size function updateCanvasSize() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; } // Generate points in a circle pattern function generatePoints() { const points: [number, number][] = []; const centerX = canvas.width / 2; const centerY = canvas.height / 2; const size = Math.min(canvas.width, canvas.height) * 0.2; const gridSize = 15; for (let x = 0; x < canvas.width; x += gridSize) { for (let y = 0; y < canvas.height; y += gridSize) { const dx = x - centerX; const dy = y - centerY; const distance = Math.sqrt(dx * dx + dy * dy); if (distance > size) { points.push([x, y]); } } } return points; } // Initialize particles function initParticles() { const points = generatePoints(); particles = points.map(([targetX, targetY]) => { const angle = Math.random() * Math.PI * 2; const distance = Math.random() * 200; return { x: targetX + Math.cos(angle) * distance, y: targetY + Math.sin(angle) * distance, vx: (Math.random() - 0.5) * 2, vy: (Math.random() - 0.5) * 2, targetX, targetY, radius: Math.random() * 1.5 + 1, color: Math.random() < 0.5 ? primaryColor : secondaryColor }; }); } // Animation loop function animate() { if (!ctx) return; ctx.fillStyle = "rgba(0, 0, 0, 0.2)"; ctx.fillRect(0, 0, canvas.width, canvas.height); particles.forEach(particle => { // Mouse repulsion const dx = mouse.x - particle.x; const dy = mouse.y - particle.y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < mouse.radius) { const force = (mouse.radius - distance) / mouse.radius; const angle = Math.atan2(dy, dx); particle.vx -= Math.cos(angle) * force * 2; particle.vy -= Math.sin(angle) * force * 2; } // Target attraction const tdx = particle.targetX - particle.x; const tdy = particle.targetY - particle.y; const targetDistance = Math.sqrt(tdx * tdx + tdy * tdy); if (targetDistance > 1) { particle.vx += (tdx / targetDistance) * 0.2; particle.vy += (tdy / targetDistance) * 0.2; } // Update position particle.x += particle.vx; particle.y += particle.vy; // Apply drag particle.vx *= 0.95; particle.vy *= 0.95; // Draw particle ctx.beginPath(); ctx.arc(particle.x, particle.y, particle.radius, 0, Math.PI * 2); ctx.fillStyle = particle.color; ctx.fill(); }); animationFrame = requestAnimationFrame(animate); } // Event handlers function handleMouseMove(e: MouseEvent) { const rect = canvas.getBoundingClientRect(); mouse.x = e.clientX - rect.left; mouse.y = e.clientY - rect.top; } // Initialize updateCanvasSize(); initParticles(); animate(); // Add event listeners window.addEventListener("mousemove", handleMouseMove); window.addEventListener("resize", () => { updateCanvasSize(); initParticles(); }); // Cleanup function return () => { cancelAnimationFrame(animationFrame); window.removeEventListener("mousemove", handleMouseMove); window.removeEventListener("resize", updateCanvasSize); }; } }; }

Changelog.

Version 2.1.5

Jun 16, 2024

We have introduced real-time keyword performance tracking, allowing users to monitor keyword effectiveness throughout the day. Enhancements were made to the keyword optimization dashboard to improve navigation and data presentation, along with bug fixes to ensure user settings are consistently saved between sessions.

Moreover, a new multilingual support feature has been launched, enhancing content optimization for global audiences. We’ve also increased the efficiency of our site audit tool, reducing average audit times by 30%. A critical issue affecting the loading of historical SEO data for some users has been addressed.

Version 2.1.4

May 18, 2024

In this update, we’ve rolled out several enhancements that streamline the user experience and boost functionality. Key improvements include an upgraded analytics engine for faster, more accurate data processing, and a new feature that allows users to customize their dashboard layouts for a more personalized approach to SEO monitoring. Additionally, we’ve fixed a bug affecting mobile responsiveness, ensuring a smoother interface across all devices. This update reaffirms our commitment to providing a top-tier SEO tool that meets the evolving needs of our users.