379 lines
11 KiB
JavaScript
379 lines
11 KiB
JavaScript
/* ===== 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 = `
|
|
<div class="furry-loading furry-loading-lg"></div>
|
|
<div style="margin-top: 0.5rem; font-size: 0.8rem; color: var(--furry-text-secondary);">
|
|
🐾 Lade Bild...
|
|
</div>
|
|
`;
|
|
|
|
// 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 = `
|
|
<div style="font-size: 3rem; margin-bottom: 1rem;">🐾</div>
|
|
<div style="color: var(--furry-text-secondary); text-align: center;">
|
|
<div style="font-weight: 600; margin-bottom: 0.5rem;">Bild konnte nicht geladen werden</div>
|
|
<div style="font-size: 0.8rem;">Versuche es später erneut</div>
|
|
</div>
|
|
`;
|
|
|
|
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;
|
|
}
|