Add awesome animations: scroll reveal, ripple effects, hover lifts, gradient shifts, floating icons
This commit is contained in:
+73
@@ -98,8 +98,81 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
openLoginModal();
|
||||
});
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// SCROLL-TRIGGERED ANIMATIONS
|
||||
// ========================================
|
||||
// Fade-in sections on scroll (Intersection Observer)
|
||||
initScrollReveal();
|
||||
|
||||
// Ripple effect on buttons
|
||||
initRippleEffects();
|
||||
});
|
||||
|
||||
// ========================================
|
||||
// SCROLL REVEAL (Intersection Observer)
|
||||
// ========================================
|
||||
function initScrollReveal() {
|
||||
// Respect reduced motion preference
|
||||
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
if (prefersReducedMotion) return;
|
||||
|
||||
const observerOptions = {
|
||||
root: null,
|
||||
rootMargin: '0px 0px -60px 0px',
|
||||
threshold: 0.1
|
||||
};
|
||||
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('visible');
|
||||
observer.unobserve(entry.target); // Only animate once
|
||||
}
|
||||
});
|
||||
}, observerOptions);
|
||||
|
||||
// Observe all sections for reveal animation
|
||||
document.querySelectorAll('.section, .telegram-banner, #newsletter-section').forEach(section => {
|
||||
// Add initial invisible state for smooth reveal
|
||||
section.style.opacity = '0';
|
||||
section.style.transform = 'translateY(20px)';
|
||||
section.style.transition = 'opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1), transform 0.6s cubic-bezier(0.16, 1, 0.3, 1)';
|
||||
observer.observe(section);
|
||||
});
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// RIPPLE EFFECT ON BUTTONS
|
||||
// ========================================
|
||||
function initRippleEffects() {
|
||||
document.querySelectorAll('.btn, .lang-btn').forEach(btn => {
|
||||
btn.addEventListener('click', function(e) {
|
||||
const rect = this.getBoundingClientRect();
|
||||
const x = e.clientX - rect.left;
|
||||
const y = e.clientY - rect.top;
|
||||
|
||||
const ripple = document.createElement('span');
|
||||
ripple.style.cssText = `
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
width: 0;
|
||||
height: 0;
|
||||
left: ${x}px;
|
||||
top: ${y}px;
|
||||
transform: translate(-50%, -50%);
|
||||
pointer-events: none;
|
||||
animation: rippleExpand 0.6s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||
`;
|
||||
this.style.position = this.style.position || 'relative';
|
||||
this.style.overflow = 'hidden';
|
||||
this.appendChild(ripple);
|
||||
setTimeout(() => ripple.remove(), 600);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// LANGUAGE
|
||||
// ========================================
|
||||
|
||||
Reference in New Issue
Block a user