Spaces:
Sleeping
Sleeping
| /** | |
| * βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| * SMOOTH ANIMATIONS & MICRO INTERACTIONS | |
| * Ultra Smooth, Modern Animations System | |
| * βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| */ | |
| class AnimationController { | |
| constructor() { | |
| this.init(); | |
| } | |
| init() { | |
| this.setupMicroAnimations(); | |
| this.setupSliderAnimations(); | |
| this.setupButtonAnimations(); | |
| this.setupMenuAnimations(); | |
| this.setupScrollAnimations(); | |
| } | |
| /** | |
| * Micro Animations - Subtle feedback | |
| */ | |
| setupMicroAnimations() { | |
| // Add micro-bounce to interactive elements | |
| document.querySelectorAll('button, .nav-button, .stat-card, .glass-card').forEach(el => { | |
| el.addEventListener('click', (e) => { | |
| el.classList.add('micro-bounce'); | |
| setTimeout(() => el.classList.remove('micro-bounce'), 600); | |
| }); | |
| }); | |
| // Add micro-scale on hover for cards | |
| document.querySelectorAll('.stat-card, .glass-card').forEach(card => { | |
| card.addEventListener('mouseenter', () => { | |
| card.style.transition = 'transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)'; | |
| }); | |
| }); | |
| } | |
| /** | |
| * Slider with smooth feedback | |
| */ | |
| setupSliderAnimations() { | |
| document.querySelectorAll('.slider-container').forEach(container => { | |
| const track = container.querySelector('.slider-track'); | |
| const thumb = container.querySelector('.slider-thumb'); | |
| const fill = container.querySelector('.slider-fill'); | |
| const input = container.querySelector('input[type="range"]'); | |
| if (!input) return; | |
| let isDragging = false; | |
| const updateSlider = (value) => { | |
| const min = parseFloat(input.min) || 0; | |
| const max = parseFloat(input.max) || 100; | |
| const percentage = ((value - min) / (max - min)) * 100; | |
| if (fill) fill.style.width = `${percentage}%`; | |
| if (thumb) thumb.style.left = `${percentage}%`; | |
| }; | |
| input.addEventListener('input', (e) => { | |
| updateSlider(e.target.value); | |
| // Add feedback pulse | |
| container.classList.add('feedback-pulse'); | |
| setTimeout(() => container.classList.remove('feedback-pulse'), 300); | |
| }); | |
| // Mouse drag | |
| if (thumb) { | |
| thumb.addEventListener('mousedown', (e) => { | |
| isDragging = true; | |
| e.preventDefault(); | |
| }); | |
| document.addEventListener('mousemove', (e) => { | |
| if (!isDragging) return; | |
| const rect = track.getBoundingClientRect(); | |
| const x = e.clientX - rect.left; | |
| const percentage = Math.max(0, Math.min(100, (x / rect.width) * 100)); | |
| const min = parseFloat(input.min) || 0; | |
| const max = parseFloat(input.max) || 100; | |
| const value = min + (percentage / 100) * (max - min); | |
| input.value = value; | |
| updateSlider(value); | |
| input.dispatchEvent(new Event('input', { bubbles: true })); | |
| }); | |
| document.addEventListener('mouseup', () => { | |
| isDragging = false; | |
| }); | |
| } | |
| // Initialize | |
| updateSlider(input.value); | |
| }); | |
| } | |
| /** | |
| * 3D Button animations | |
| */ | |
| setupButtonAnimations() { | |
| document.querySelectorAll('.button-3d, button.primary, button.secondary').forEach(button => { | |
| // Ripple effect | |
| button.classList.add('feedback-ripple'); | |
| // 3D press effect | |
| button.addEventListener('mousedown', () => { | |
| button.style.transform = 'translateY(2px) scale(0.98)'; | |
| }); | |
| button.addEventListener('mouseup', () => { | |
| button.style.transform = ''; | |
| }); | |
| button.addEventListener('mouseleave', () => { | |
| button.style.transform = ''; | |
| }); | |
| }); | |
| } | |
| /** | |
| * Menu animations | |
| */ | |
| setupMenuAnimations() { | |
| // Dropdown menus | |
| document.querySelectorAll('[data-menu]').forEach(menuTrigger => { | |
| menuTrigger.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| const menu = document.querySelector(menuTrigger.dataset.menu); | |
| if (!menu) return; | |
| const isOpen = menu.classList.contains('menu-open'); | |
| // Close all menus | |
| document.querySelectorAll('.menu-dropdown').forEach(m => { | |
| m.classList.remove('menu-open'); | |
| }); | |
| // Toggle current menu | |
| if (!isOpen) { | |
| menu.classList.add('menu-open'); | |
| this.animateMenuIn(menu); | |
| } | |
| }); | |
| }); | |
| // Close menus on outside click | |
| document.addEventListener('click', (e) => { | |
| if (!e.target.closest('[data-menu]') && !e.target.closest('.menu-dropdown')) { | |
| document.querySelectorAll('.menu-dropdown').forEach(menu => { | |
| menu.classList.remove('menu-open'); | |
| }); | |
| } | |
| }); | |
| } | |
| animateMenuIn(menu) { | |
| menu.style.opacity = '0'; | |
| menu.style.transform = 'translateY(-10px) scale(0.95)'; | |
| // Use setTimeout instead of requestAnimationFrame to avoid performance warnings | |
| // requestAnimationFrame can trigger warnings if handler takes too long | |
| setTimeout(() => { | |
| menu.style.transition = 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)'; | |
| menu.style.opacity = '1'; | |
| menu.style.transform = 'translateY(0) scale(1)'; | |
| }, 0); | |
| } | |
| /** | |
| * Scroll animations | |
| */ | |
| setupScrollAnimations() { | |
| const observerOptions = { | |
| threshold: 0.1, | |
| rootMargin: '0px 0px -50px 0px' | |
| }; | |
| const observer = new IntersectionObserver((entries) => { | |
| entries.forEach(entry => { | |
| if (entry.isIntersecting) { | |
| entry.target.classList.add('animate-in'); | |
| } | |
| }); | |
| }, observerOptions); | |
| document.querySelectorAll('.stat-card, .glass-card, .section').forEach(el => { | |
| observer.observe(el); | |
| }); | |
| } | |
| /** | |
| * Add smooth transitions to elements | |
| */ | |
| addSmoothTransition(element, property = 'all') { | |
| element.style.transition = `${property} 0.3s cubic-bezier(0.4, 0, 0.2, 1)`; | |
| } | |
| } | |
| // Initialize animations when DOM is ready | |
| if (document.readyState === 'loading') { | |
| document.addEventListener('DOMContentLoaded', () => { | |
| window.animationController = new AnimationController(); | |
| }); | |
| } else { | |
| window.animationController = new AnimationController(); | |
| } | |