171 lines
5.7 KiB
JavaScript
171 lines
5.7 KiB
JavaScript
/**
|
|
* Ultra-Smooth Scroll Header - Butter Performance
|
|
* Uses advanced techniques for maximum smoothness
|
|
*/
|
|
|
|
document.addEventListener("DOMContentLoaded", function() {
|
|
const topBanner = document.querySelector('.top-banner');
|
|
const slideMenu = document.querySelector('.slide-menu');
|
|
|
|
if (!topBanner) return;
|
|
|
|
// Configuration for ultra-smooth performance
|
|
const scrollThreshold = 50;
|
|
const rafDelay = 8; // 120fps for ultra-smooth
|
|
const transitionDuration = 400; // Slightly longer for smoother feel
|
|
|
|
// State tracking with performance optimization
|
|
let isScrolled = false;
|
|
let lastScrollY = 0;
|
|
let scrollDirection = 'down';
|
|
let rafId = null;
|
|
let lastUpdateTime = 0;
|
|
let scrollVelocity = 0;
|
|
let lastScrollTime = 0;
|
|
|
|
// Calculate scroll velocity for smoother transitions
|
|
function calculateVelocity(currentScrollY, currentTime) {
|
|
if (lastScrollTime === 0) {
|
|
lastScrollTime = currentTime;
|
|
return 0;
|
|
}
|
|
|
|
const timeDelta = currentTime - lastScrollTime;
|
|
const scrollDelta = Math.abs(currentScrollY - lastScrollY);
|
|
const velocity = scrollDelta / timeDelta;
|
|
|
|
lastScrollTime = currentTime;
|
|
return velocity;
|
|
}
|
|
|
|
// Ultra-smooth header state update with velocity-based easing
|
|
function updateHeaderState(scrolled, velocity = 0) {
|
|
if (scrolled === isScrolled) return;
|
|
|
|
// Add velocity-based class for different transition speeds
|
|
if (velocity > 5) {
|
|
topBanner.classList.add('fast-scroll');
|
|
} else {
|
|
topBanner.classList.remove('fast-scroll');
|
|
}
|
|
|
|
// Use requestAnimationFrame for smooth DOM updates
|
|
requestAnimationFrame(() => {
|
|
if (scrolled) {
|
|
topBanner.classList.add('scrolled');
|
|
if (slideMenu) {
|
|
slideMenu.style.transition = 'top 0.3s cubic-bezier(0.4, 0, 0.2, 1)';
|
|
slideMenu.style.top = '80px';
|
|
}
|
|
} else {
|
|
topBanner.classList.remove('scrolled');
|
|
if (slideMenu) {
|
|
slideMenu.style.transition = 'top 0.3s cubic-bezier(0.4, 0, 0.2, 1)';
|
|
slideMenu.style.top = '110px';
|
|
}
|
|
}
|
|
});
|
|
|
|
isScrolled = scrolled;
|
|
}
|
|
|
|
// Advanced scroll handler with velocity detection
|
|
function handleScroll(currentTime) {
|
|
// Ultra-high frequency throttling
|
|
if (currentTime - lastUpdateTime < rafDelay) {
|
|
rafId = requestAnimationFrame(handleScroll);
|
|
return;
|
|
}
|
|
|
|
const currentScrollY = window.pageYOffset || document.documentElement.scrollTop;
|
|
const velocity = calculateVelocity(currentScrollY, currentTime);
|
|
|
|
// Detect scroll direction with hysteresis for stability
|
|
const scrollDelta = currentScrollY - lastScrollY;
|
|
if (Math.abs(scrollDelta) > 1) {
|
|
scrollDirection = scrollDelta > 0 ? 'down' : 'up';
|
|
}
|
|
|
|
// Apply hysteresis to prevent flickering
|
|
let shouldScroll;
|
|
if (scrollDirection === 'down') {
|
|
shouldScroll = currentScrollY > scrollThreshold + 10;
|
|
} else {
|
|
shouldScroll = currentScrollY > scrollThreshold - 10;
|
|
}
|
|
|
|
updateHeaderState(shouldScroll, velocity);
|
|
|
|
lastScrollY = currentScrollY;
|
|
lastUpdateTime = currentTime;
|
|
rafId = null;
|
|
}
|
|
|
|
// Optimized scroll listener with passive event
|
|
function onScroll() {
|
|
if (!rafId) {
|
|
rafId = requestAnimationFrame(handleScroll);
|
|
}
|
|
}
|
|
|
|
// Add scroll listener with maximum performance
|
|
window.addEventListener('scroll', onScroll, {
|
|
passive: true,
|
|
capture: false
|
|
});
|
|
|
|
// Handle resize with debouncing
|
|
let resizeTimeout;
|
|
function onResize() {
|
|
clearTimeout(resizeTimeout);
|
|
resizeTimeout = setTimeout(() => {
|
|
lastScrollY = window.pageYOffset || document.documentElement.scrollTop;
|
|
updateHeaderState(lastScrollY > scrollThreshold);
|
|
}, 100);
|
|
}
|
|
|
|
window.addEventListener('resize', onResize, { passive: true });
|
|
|
|
// Handle visibility change to pause/resume animations
|
|
function onVisibilityChange() {
|
|
if (document.hidden) {
|
|
if (rafId) {
|
|
cancelAnimationFrame(rafId);
|
|
rafId = null;
|
|
}
|
|
} else {
|
|
lastScrollY = window.pageYOffset || document.documentElement.scrollTop;
|
|
updateHeaderState(lastScrollY > scrollThreshold);
|
|
}
|
|
}
|
|
|
|
document.addEventListener('visibilitychange', onVisibilityChange);
|
|
|
|
// Initialize with smooth transition
|
|
requestAnimationFrame(() => {
|
|
updateHeaderState(window.pageYOffset > scrollThreshold);
|
|
});
|
|
|
|
// Add smooth scroll behavior to internal links
|
|
document.querySelectorAll('a[href^="#"]').forEach(link => {
|
|
link.addEventListener('click', function(e) {
|
|
const targetId = this.getAttribute('href');
|
|
if (targetId === '#') return;
|
|
|
|
const targetElement = document.querySelector(targetId);
|
|
if (targetElement) {
|
|
e.preventDefault();
|
|
|
|
// Smooth scroll with header offset
|
|
const headerHeight = topBanner.offsetHeight;
|
|
const targetPosition = targetElement.offsetTop - headerHeight - 20;
|
|
|
|
window.scrollTo({
|
|
top: targetPosition,
|
|
behavior: 'smooth'
|
|
});
|
|
}
|
|
});
|
|
});
|
|
});
|