Really-amin's picture
Upload 303 files
b068b76 verified
/**
* ═══════════════════════════════════════════════════════════════════
* 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();
}