feat: implement lazy loading for product images to enhance performance

- Added CSS styles for lazy loading images, improving initial load times and user experience.
- Updated ProductManager class to initialize lazy loading functionality, utilizing Intersection Observer for efficient image loading as they come into view.
- Modified image elements in product catalog to support lazy loading with appropriate data attributes.
This commit is contained in:
George Birikorang 2025-09-19 09:54:06 -07:00
parent 0a4494bc44
commit aa1d74f3cb
2 changed files with 59 additions and 3 deletions

View file

@ -15,6 +15,16 @@
display: flex !important;
}
}
/* Lazy loading styles */
.lazy-load {
opacity: 0.7;
transition: opacity 0.3s ease-in-out;
}
.lazy-load.loaded {
opacity: 1;
}
</style>
<link
href="https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700&family=Playfair+Display:wght@100;200;300;400;500;600;700&family=Poppins:wght@400;500;600&display=swap"

View file

@ -124,9 +124,10 @@ class ProductManager {
this.updateResultsCount();
this.updatePagination();
// Re-add image enlargement listeners after products are rendered
// Re-add image enlargement listeners and lazy loading after products are rendered
setTimeout(() => {
addImageEnlargementListeners();
this.initLazyLoading();
}, 50);
}
@ -143,10 +144,12 @@ class ProductManager {
}">
<div class="relative h-80 overflow-hidden">
<img
src="${product.image}"
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='320' viewBox='0 0 400 320'%3E%3Crect width='400' height='320' fill='%23f3f4f6'/%3E%3C/svg%3E"
data-src="${product.image}"
alt="${product.alt}"
class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300 cursor-pointer"
class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300 cursor-pointer lazy-load"
data-enlarge-src="${product.image}"
loading="lazy"
/>
<!-- Hover Overlay -->
<div class="absolute inset-0 bg-dark-charcoal bg-opacity-70 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex items-center justify-center">
@ -441,6 +444,49 @@ class ProductManager {
this.updateFilterButtonText();
});
}
// Initialize lazy loading
this.initLazyLoading();
}
// Initialize lazy loading for images
initLazyLoading() {
// Use Intersection Observer for better performance
if ("IntersectionObserver" in window) {
const imageObserver = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const img = entry.target;
if (img.dataset.src) {
img.src = img.dataset.src;
img.classList.remove("lazy-load");
img.classList.add("loaded");
observer.unobserve(img);
}
}
});
},
{
rootMargin: "50px 0px", // Start loading 50px before image comes into view
threshold: 0.01,
}
);
// Observe all lazy-loaded images
const lazyImages = document.querySelectorAll(".lazy-load");
lazyImages.forEach((img) => imageObserver.observe(img));
} else {
// Fallback for older browsers - load all images immediately
const lazyImages = document.querySelectorAll(".lazy-load");
lazyImages.forEach((img) => {
if (img.dataset.src) {
img.src = img.dataset.src;
img.classList.remove("lazy-load");
img.classList.add("loaded");
}
});
}
}
// Update results count