// App JS ohne Alpine-Initialisierung. Alpine wird von Livewire verwaltet. // Premium Scroll Animations with Intersection Observer (function() { 'use strict'; // Globaler Observer, der wiederverwendet wird let globalObserver = null; // Warte bis DOM vollständig geladen ist if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } function init() { initAnimations(); // Scroll Progress Indicator initScrollProgress(); // Premium Sticky Header initStickyHeader(); // Livewire Event Listener für dynamisch geladene Komponenten document.addEventListener('livewire:navigated', function() { initAnimations(); }); // Fallback für ältere Livewire-Versionen document.addEventListener('livewire:load', function() { initAnimations(); }); // Nach Livewire-Updates window.addEventListener('livewire:update', function() { setTimeout(() => { initAnimations(); }, 100); }); } function initAnimations() { // Intersection Observer Konfiguration const observerOptions = { threshold: 0.15, rootMargin: '0px 0px -80px 0px' }; // Erstelle Observer wenn noch nicht vorhanden if (!globalObserver) { globalObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { // Füge is-visible Klasse mit kleinem Delay hinzu für sanfteren Effekt setTimeout(() => { entry.target.classList.add('is-visible'); }, 50); // Observer beenden nach Animation für bessere Performance globalObserver.unobserve(entry.target); } }); }, observerOptions); } // Finde alle Elemente mit Animation-Klassen, die noch nicht beobachtet werden const animatedElements = document.querySelectorAll( '.scroll-animate:not(.is-visible):not(.observed), .fade-in:not(.is-visible):not(.observed), .slide-up:not(.is-visible):not(.observed), .slide-right:not(.is-visible):not(.observed), .slide-left:not(.is-visible):not(.observed), .scale-in:not(.is-visible):not(.observed), .slide-down:not(.is-visible):not(.observed)' ); // Beobachte jedes Element animatedElements.forEach(el => { el.classList.add('observed'); // Markiere als beobachtet globalObserver.observe(el); }); // Smooth Scroll für Anchor-Links (nur einmal registrieren) if (!window.smoothScrollInitialized) { document.addEventListener('click', function(e) { const target = e.target.closest('a[href^="#"]'); if (target && target.hash) { const targetElement = document.querySelector(target.hash); if (targetElement) { e.preventDefault(); const headerOffset = 80; const elementPosition = targetElement.getBoundingClientRect().top; const offsetPosition = elementPosition + window.pageYOffset - headerOffset; window.scrollTo({ top: offsetPosition, behavior: 'smooth' }); } } }); window.smoothScrollInitialized = true; } } function initStickyHeader() { const header = document.getElementById('main-header'); if (!header) return; let lastScrollTop = 0; let scrollTimeout = null; function handleHeaderScroll() { const scrollTop = window.pageYOffset || document.documentElement.scrollTop; // Add/Remove scrolled class for enhanced shadow if (scrollTop > 50) { header.classList.add('scrolled'); } else { header.classList.remove('scrolled'); } // Optional: Hide header on scroll down, show on scroll up // Uncomment if you want auto-hide behavior /* if (scrollTop > lastScrollTop && scrollTop > 100) { // Scrolling down header.classList.add('hide'); } else { // Scrolling up header.classList.remove('hide'); } */ lastScrollTop = scrollTop <= 0 ? 0 : scrollTop; } // Listen to scroll with throttling let headerTicking = false; window.addEventListener('scroll', function() { if (!headerTicking) { window.requestAnimationFrame(function() { handleHeaderScroll(); headerTicking = false; }); headerTicking = true; } }); // Initial check handleHeaderScroll(); } function initScrollProgress() { // Erstelle Progress Bar Element const progressBar = document.createElement('div'); progressBar.className = 'scroll-progress-bar'; document.body.appendChild(progressBar); // Update Progress on Scroll function updateProgress() { const windowHeight = window.innerHeight; const documentHeight = document.documentElement.scrollHeight; const scrollTop = window.pageYOffset || document.documentElement.scrollTop; // Berechne Progress (0-100%) const scrollPercentage = (scrollTop / (documentHeight - windowHeight)) * 100; // Update Bar Width progressBar.style.width = `${Math.min(scrollPercentage, 100)}%`; // Optional: Show/Hide basierend auf Scroll-Position if (scrollTop > 100) { progressBar.classList.add('visible'); } else { progressBar.classList.remove('visible'); } } // Listen to scroll events mit Throttling für Performance let ticking = false; window.addEventListener('scroll', function() { if (!ticking) { window.requestAnimationFrame(function() { updateProgress(); ticking = false; }); ticking = true; } }); // Initial update updateProgress(); } })();