// Animated Hosting Banner Component with D3.js function createAnimatedHostingBanner() { const bannerId = 'hosting-banner-' + Date.now(); const bannerHTML = `
`; // Add styles const styles = ` `; return styles + bannerHTML; } // Function to close the banner function closeAnimatedBanner(bannerId) { const banner = document.getElementById(bannerId); if (banner) { banner.style.animation = 'bannerSlideDown 0.5s ease-in forwards'; setTimeout(() => { banner.classList.add('hidden'); localStorage.setItem('hostingBannerClosed', 'true'); localStorage.setItem('hostingBannerClosedDate', new Date().toISOString()); }, 500); } } // Add slide down animation const slideDownStyle = document.createElement('style'); slideDownStyle.textContent = ` @keyframes bannerSlideDown { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(50px); } } `; document.head.appendChild(slideDownStyle); // Function to initialize D3.js animations function initializeBannerAnimations(bannerId) { // Only run if D3 is available if (typeof d3 === 'undefined') { console.log('D3.js not loaded, skipping animations'); return; } const svg = d3.select(`#banner-svg-${bannerId}`); const width = 1200; const height = 200; svg.attr('viewBox', `0 0 ${width} ${height}`) .attr('preserveAspectRatio', 'xMidYMid slice'); // Create animated particles const particleCount = 30; const particles = Array.from({length: particleCount}, (_, i) => ({ id: i, x: Math.random() * width, y: Math.random() * height, r: Math.random() * 3 + 1, dx: (Math.random() - 0.5) * 2, dy: (Math.random() - 0.5) * 2, opacity: Math.random() * 0.5 + 0.2 })); // Add gradient definitions const defs = svg.append('defs'); const gradient = defs.append('linearGradient') .attr('id', `particle-gradient-${bannerId}`) .attr('x1', '0%') .attr('y1', '0%') .attr('x2', '100%') .attr('y2', '100%'); gradient.append('stop') .attr('offset', '0%') .style('stop-color', '#FF6B35') .style('stop-opacity', 0.8); gradient.append('stop') .attr('offset', '100%') .style('stop-color', '#FF8A50') .style('stop-opacity', 0.4); // Create particle elements const particleElements = svg.selectAll('circle') .data(particles) .enter() .append('circle') .attr('cx', d => d.x) .attr('cy', d => d.y) .attr('r', d => d.r) .attr('fill', `url(#particle-gradient-${bannerId})`) .attr('opacity', d => d.opacity); // Animate particles function animateParticles() { particles.forEach(p => { p.x += p.dx; p.y += p.dy; // Wrap around edges if (p.x < 0) p.x = width; if (p.x > width) p.x = 0; if (p.y < 0) p.y = height; if (p.y > height) p.y = 0; }); particleElements .data(particles) .attr('cx', d => d.x) .attr('cy', d => d.y); requestAnimationFrame(animateParticles); } animateParticles(); // Add connection lines between nearby particles const connectionThreshold = 100; function updateConnections() { // Remove existing lines svg.selectAll('line').remove(); // Add new connections for (let i = 0; i < particles.length; i++) { for (let j = i + 1; j < particles.length; j++) { const dx = particles[i].x - particles[j].x; const dy = particles[i].y - particles[j].y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < connectionThreshold) { const opacity = (1 - distance / connectionThreshold) * 0.2; svg.append('line') .attr('x1', particles[i].x) .attr('y1', particles[i].y) .attr('x2', particles[j].x) .attr('y2', particles[j].y) .attr('stroke', '#FF6B35') .attr('stroke-width', 0.5) .attr('opacity', opacity); } } } setTimeout(updateConnections, 100); } updateConnections(); } // Check if banner should be shown function shouldShowAnimatedBanner() { const currentPath = window.location.pathname; const isHostingPage = currentPath.includes('/odoo-hosting/'); const bannerClosed = localStorage.getItem('hostingBannerClosed'); const closedDate = localStorage.getItem('hostingBannerClosedDate'); // Reset after 24 hours if (closedDate) { const dayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000); if (new Date(closedDate) < dayAgo) { localStorage.removeItem('hostingBannerClosed'); localStorage.removeItem('hostingBannerClosedDate'); return isHostingPage; } } return isHostingPage && bannerClosed !== 'true'; } // Auto-initialize when DOM is ready if (typeof window !== 'undefined') { // Wait for DOM and D3 to be ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', function() { setTimeout(() => { const bannerId = 'hosting-banner-' + Date.now(); if (shouldShowAnimatedBanner()) { initializeBannerAnimations(bannerId); } }, 100); }); } else { // DOM is already ready setTimeout(() => { const bannerId = 'hosting-banner-' + Date.now(); if (shouldShowAnimatedBanner()) { initializeBannerAnimations(bannerId); } }, 100); } }