PageSourceSearch

https://drizzleinfotainment.com/js/homepage-enhancer.js?v=1790647141

js drizzleinfotainment.com collected 2026-09-29 01:59:15 UTC 9,539 bytes, 269 lines download raw bytes

1/**
2 * Enhanced Homepage Interactions
3 * Handles animations, particles, and interactive elements
4 */
5
6class HomepageEnhancer {
7    constructor() {
8        this.init();
9    }
10
11    init() {
12        this.initParticles();
13        this.initCounterAnimation();
14        this.initScrollEffects();
15        this.initHeroInteractions();
16        this.initPerformanceOptimizations();
17    }
18
19    // Create dynamic particles background
20    initParticles() {
21        const particlesContainer = document.getElementById('hero-particles');
22        if (!particlesContainer) return;
23
24        const particleCount = window.innerWidth > 768 ? 60 : 25;
25        particlesContainer.innerHTML = ''; // Clear existing particles
26
27        for (let i = 0; i < particleCount; i++) {
28            const particle = document.createElement('div');
29            particle.className = 'particle';
30            
31            // Random positioning and timing
32            particle.style.left = Math.random() * 100 + '%';
33            particle.style.animationDelay = Math.random() * 8 + 's';
34            particle.style.animationDuration = (Math.random() * 6 + 6) + 's';
35            
36            // Random size and opacity variation
37            const size = Math.random() * 2 + 2;
38            particle.style.width = size + 'px';
39            particle.style.height = size + 'px';
40            particle.style.opacity = Math.random() * 0.5 + 0.3;
41            
42            particlesContainer.appendChild(particle);
43        }
44    }
45
46    // Enhanced counter animation with easing
47    initCounterAnimation() {
48        const counters = document.querySelectorAll('[data-count]');
49        
50        counters.forEach(counter => {
51            const targetCount = parseInt(counter.dataset.count);
52            let currentCount = 0;
53            let hasAnimated = false;
54
55            // Intersection Observer for triggering animation when in view
56            const observer = new IntersectionObserver((entries) => {
57                entries.forEach(entry => {
58                    if (entry.isIntersecting && !hasAnimated) {
59                        this.animateCounter(counter, targetCount);
60                        hasAnimated = true;
61                    }
62                });
63            }, { threshold: 0.3 });
64
65            observer.observe(counter);
66        });
67    }
68
69    animateCounter(element, target) {
70        let start = 0;
71        const duration = 2500;
72        const startTime = performance.now();
73
74        const animate = (currentTime) => {
75            const elapsed = currentTime - startTime;
76            const progress = Math.min(elapsed / duration, 1);
77            
78            // Easing function for smooth animation
79            const easeOutQuart = 1 - Math.pow(1 - progress, 4);
80            const current = Math.floor(easeOutQuart * target);
81            
82            element.textContent = current.toLocaleString();
83            element.classList.add('counting');
84            
85            setTimeout(() => {
86                element.classList.remove('counting');
87            }, 100);
88
89            if (progress < 1) {
90                requestAnimationFrame(animate);
91            }
92        };
93
94        requestAnimationFrame(animate);
95    }
96
97    // Scroll effects and parallax
98    initScrollEffects() {
99        let ticking = false;
100
101        const handleScroll = () => {
102            if (!ticking) {
103                requestAnimationFrame(() => {
104                    this.updateScrollEffects();
105                    ticking = false;
106                });
107                ticking = true;
108            }
109        };
110
111        window.addEventListener('scroll', handleScroll, { passive: true });
112    }
113
114    updateScrollEffects() {
115        const scrolled = window.pageYOffset;
116        const hero = document.querySelector('.page-banner.home');
117        
118        if (hero && window.innerWidth > 768) {
119            // Parallax effect
120            const parallax = scrolled * 0.5;
121            hero.style.transform = `translateY(${parallax}px)`;
122            
123            // Fade effect on scroll
124            const heroHeight = hero.offsetHeight;
125            const opacity = Math.max(0, 1 - (scrolled / heroHeight) * 1.5);
126            hero.style.opacity = opacity;
127        }
128
129        // Fade in elements on scroll
130        const elements = document.querySelectorAll('.fade-in-on-scroll');
131        elements.forEach(element => {
132            const elementTop = element.getBoundingClientRect().top;
133            const elementVisible = 150;
134
135            if (elementTop < window.innerHeight - elementVisible) {
136                element.classList.add('active');
137            }
138        });
139    }
140
141    // Hero section interactions
142    initHeroInteractions() {
143        // Smooth scroll to next section
144        window.scrollToNextSection = () => {
145            const nextSection = document.querySelector('.counter-section') || 
146                              document.querySelector('section:nth-of-type(2)');
147            if (nextSection) {
148                nextSection.scrollIntoView({ 
149                    behavior: 'smooth',
150                    block: 'start'
151                });
152            }
153        };
154
155        // Hero button hover effects
156        const heroButtons = document.querySelectorAll('.btn-hero-primary, .btn-hero-secondary');
157        heroButtons.forEach(button => {
158            button.addEventListener('mouseenter', () => {
159                button.style.transform = 'translateY(-3px) scale(1.05)';
160            });
161
162            button.addEventListener('mouseleave', () => {
163                button.style.transform = 'translateY(0) scale(1)';
164            });
165        });
166
167        // Interactive particle effects on mouse move
168        const hero = document.querySelector('.page-banner.home');
169        if (hero && window.innerWidth > 768) {
170            hero.addEventListener('mousemove', (e) => {
171                const particles = document.querySelectorAll('.particle');
172                const mouseX = e.clientX / window.innerWidth;
173                const mouseY = e.clientY / window.innerHeight;
174
175                particles.forEach((particle, index) => {
176                    const speed = (index % 3 + 1) * 0.5;
177                    const x = (mouseX - 0.5) * speed;
178                    const y = (mouseY - 0.5) * speed;
179                    
180                    particle.style.transform = `translate(${x}px, ${y}px)`;
181                });
182            });
183        }
184    }
185
186    // Performance optimizations
187    initPerformanceOptimizations() {
188        // Lazy loading for images
189        if ('IntersectionObserver' in window) {
190            const imageObserver = new IntersectionObserver((entries, observer) => {
191                entries.forEach(entry => {
192                    if (entry.isIntersecting) {
193                        const img = entry.target;
194                        img.src = img.dataset.src;
195                        img.classList.remove('lazy');
196                        imageObserver.unobserve(img);
197                    }
198                });
199            });
200
201            document.querySelectorAll('img[data-src]').forEach(img => {
202                imageObserver.observe(img);
203            });
204        }
205
206        // Reduce animations on low-end devices
207        if (navigator.hardwareConcurrency && navigator.hardwareConcurrency < 4) {
208            document.documentElement.classList.add('reduced-motion');
209        }
210
211        // Pause animations when tab is not visible
212        document.addEventListener('visibilitychange', () => {
213            const animations = document.querySelectorAll('.particle, .counter-section::before');
214            animations.forEach(element => {
215                if (document.hidden) {
216                    element.style.animationPlayState = 'paused';
217                } else {
218                    element.style.animationPlayState = 'running';
219                }
220            });
221        });
222    }
223
224    // Responsive particle management
225    handleResize() {
226        this.initParticles();
227    }
228}
229
230// Initialize when DOM is loaded
231document.addEventListener('DOMContentLoaded', () => {
232    const enhancer = new HomepageEnhancer();
233    
234    // Handle window resize
235    window.addEventListener('resize', () => {
236        enhancer.handleResize();
237    });
238});
239
240// Smooth scroll polyfill for older browsers
241if (!Element.prototype.scrollIntoView) {
242    Element.prototype.scrollIntoView = function(options) {
243        const element = this;
244        const targetPosition = element.offsetTop;
245        
246        if (options && options.behavior === 'smooth') {
247            const startPosition = window.pageYOffset;
248            const distance = targetPosition - startPosition;
249            const duration = 800;
250            let start = null;
251
252            function animation(currentTime) {
253                if (start === null) start = currentTime;
254                const timeElapsed = currentTime - start;
255                const progress = Math.min(timeElapsed / duration, 1);
256                
257                window.scrollTo(0, startPosition + distance * progress);
258                
259                if (timeElapsed < duration) {
260                    requestAnimationFrame(animation);
261                }
262            }
263            
264            requestAnimationFrame(animation);
265        } else {
266            window.scrollTo(0, targetPosition);
267        }
268    };
269}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.