/* ===== FURRY LAZY LOADING ===== */ /* Moderne Lazy Loading für kasico.de */ class FurryLazyLoading { constructor() { this.images = []; this.observer = null; this.init(); } init() { // Intersection Observer API Support prüfen if ('IntersectionObserver' in window) { this.setupIntersectionObserver(); } else { // Fallback für ältere Browser this.setupScrollListener(); } // Initial alle Bilder finden this.findImages(); } setupIntersectionObserver() { this.observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { this.loadImage(entry.target); this.observer.unobserve(entry.target); } }); }, { rootMargin: '50px 0px', // 50px vor dem Viewport laden threshold: 0.01 }); } setupScrollListener() { // Fallback für ältere Browser let ticking = false; const updateImages = () => { this.images.forEach(img => { if (this.isInViewport(img)) { this.loadImage(img); this.images = this.images.filter(i => i !== img); } }); ticking = false; }; const requestTick = () => { if (!ticking) { requestAnimationFrame(updateImages); ticking = true; } }; window.addEventListener('scroll', requestTick); window.addEventListener('resize', requestTick); // Initial check requestTick(); } findImages() { // Alle Bilder mit data-src Attribut finden const lazyImages = document.querySelectorAll('img[data-src], .furry-lazy-image'); lazyImages.forEach(img => { // Placeholder hinzufügen this.addPlaceholder(img); // Zur Beobachtungsliste hinzufügen if (this.observer) { this.observer.observe(img); } else { this.images.push(img); } }); } addPlaceholder(img) { // Furry-Theme Placeholder const placeholder = document.createElement('div'); placeholder.className = 'furry-lazy-placeholder'; placeholder.innerHTML = `
🐾 Lade Bild...
`; // Placeholder Styling placeholder.style.cssText = ` display: flex; flex-direction: column; align-items: center; justify-content: center; background: var(--furry-bg-secondary); border-radius: 0.5rem; padding: 2rem; min-height: 200px; width: 100%; height: 100%; position: absolute; top: 0; left: 0; z-index: 1; `; // Container für Bild und Placeholder const container = document.createElement('div'); container.style.position = 'relative'; container.style.width = '100%'; container.style.height = '100%'; // Bild in Container verschieben img.parentNode.insertBefore(container, img); container.appendChild(img); container.appendChild(placeholder); // Placeholder Referenz speichern img.furryPlaceholder = placeholder; // Bild unsichtbar machen bis geladen img.style.opacity = '0'; img.style.transition = 'opacity 0.3s ease'; } loadImage(img) { const src = img.dataset.src || img.src; if (!src) return; // Neues Bild erstellen für Preloading const newImg = new Image(); newImg.onload = () => { // Bild erfolgreich geladen img.src = src; img.style.opacity = '1'; // Placeholder entfernen if (img.furryPlaceholder) { img.furryPlaceholder.style.animation = 'fadeOut 0.3s ease'; setTimeout(() => { if (img.furryPlaceholder.parentNode) { img.furryPlaceholder.remove(); } }, 300); } // Event triggern this.triggerEvent('furry-image-loaded', { img, src }); }; newImg.onerror = () => { // Fehler beim Laden this.handleImageError(img); }; // Bild laden newImg.src = src; } handleImageError(img) { // Furry-Theme Error Placeholder const errorPlaceholder = document.createElement('div'); errorPlaceholder.className = 'furry-lazy-error'; errorPlaceholder.innerHTML = `
🐾
Bild konnte nicht geladen werden
Versuche es später erneut
`; errorPlaceholder.style.cssText = ` display: flex; flex-direction: column; align-items: center; justify-content: center; background: var(--furry-bg-secondary); border: 2px dashed var(--furry-border); border-radius: 0.5rem; padding: 2rem; min-height: 200px; width: 100%; height: 100%; `; // Placeholder ersetzen if (img.furryPlaceholder) { img.furryPlaceholder.replaceWith(errorPlaceholder); } // Event triggern this.triggerEvent('furry-image-error', { img }); } isInViewport(element) { const rect = element.getBoundingClientRect(); return ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth) ); } // Manuelles Laden eines Bildes loadImageNow(img) { if (this.observer) { this.observer.unobserve(img); } this.loadImage(img); } // Alle Bilder sofort laden loadAllImages() { this.images.forEach(img => this.loadImageNow(img)); this.images = []; } // Neue Bilder hinzufügen (für dynamisch geladene Inhalte) addImages(container = document) { const newImages = container.querySelectorAll('img[data-src], .furry-lazy-image'); newImages.forEach(img => { if (!img.furryPlaceholder) { this.addPlaceholder(img); if (this.observer) { this.observer.observe(img); } else { this.images.push(img); } } }); } // Performance-Optimierungen optimizeImages() { // WebP Support prüfen const webpSupported = this.checkWebPSupport(); // Responsive Images const images = document.querySelectorAll('img[data-srcset]'); images.forEach(img => { if (webpSupported) { // WebP Version verwenden const webpSrcset = img.dataset.webpSrcset; if (webpSrcset) { img.srcset = webpSrcset; } } }); } checkWebPSupport() { return new Promise(resolve => { const webP = new Image(); webP.onload = webP.onerror = () => { resolve(webP.height === 2); }; webP.src = 'data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA'; }); } // Event Handling triggerEvent(eventName, data = {}) { const event = new CustomEvent(eventName, { detail: data }); document.dispatchEvent(event); } // Cleanup destroy() { if (this.observer) { this.observer.disconnect(); } // Event Listener entfernen window.removeEventListener('scroll', this.updateImages); window.removeEventListener('resize', this.updateImages); } } // Globale Instanz erstellen const furryLazyLoading = new FurryLazyLoading(); // CSS für Animationen const lazyLoadingStyle = document.createElement('style'); lazyLoadingStyle.textContent = ` .furry-lazy-image { opacity: 0; transition: opacity 0.3s ease; } .furry-lazy-image.loaded { opacity: 1; } .furry-lazy-placeholder { background: linear-gradient(90deg, var(--furry-border) 25%, var(--furry-bg-secondary) 50%, var(--furry-border) 75%); background-size: 200% 100%; animation: furry-skeleton-loading 1.5s infinite; } @keyframes furry-skeleton-loading { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } } .furry-lazy-error { background: var(--furry-bg-secondary); border: 2px dashed var(--furry-border); color: var(--furry-text-secondary); } /* Progressive Image Loading */ .furry-progressive-image { position: relative; overflow: hidden; } .furry-progressive-image img { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; } .furry-progressive-image .low-res { filter: blur(10px); transform: scale(1.1); } .furry-progressive-image .high-res { opacity: 0; transition: opacity 0.3s ease; } .furry-progressive-image .high-res.loaded { opacity: 1; } `; document.head.appendChild(lazyLoadingStyle); // Event Listener für dynamisch geladene Inhalte document.addEventListener('DOMContentLoaded', () => { // Initial alle Bilder finden furryLazyLoading.findImages(); // Mutation Observer für dynamische Inhalte const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === 'childList') { mutation.addedNodes.forEach((node) => { if (node.nodeType === 1) { // Element node furryLazyLoading.addImages(node); } }); } }); }); observer.observe(document.body, { childList: true, subtree: true }); }); // Export für Module-System if (typeof module !== 'undefined' && module.exports) { module.exports = FurryLazyLoading; }