import { Directive, ElementRef, Input, OnInit, Renderer2 } from '@angular/core'; @Directive({ selector: 'img[appLazyLoad]', standalone: true }) export class LazyLoadImageDirective implements OnInit { @Input() appLazyLoad: string = ''; @Input() placeholder: string = 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 300"%3E%3Crect fill="%23f3f4f6" width="400" height="300"/%3E%3C/svg%3E'; private observer: IntersectionObserver | null = null; constructor( private el: ElementRef, private renderer: Renderer2 ) {} ngOnInit() { // Add loading="lazy" attribute for native lazy loading this.renderer.setAttribute(this.el.nativeElement, 'loading', 'lazy'); // Set placeholder while image loads if (this.placeholder) { this.renderer.setAttribute(this.el.nativeElement, 'src', this.placeholder); } // Add a CSS class for styling during loading this.renderer.addClass(this.el.nativeElement, 'lazy-loading'); // Use Intersection Observer for enhanced lazy loading if ('IntersectionObserver' in window) { this.observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { this.loadImage(); } }); }, { rootMargin: '50px' // Start loading 50px before image enters viewport } ); this.observer.observe(this.el.nativeElement); } else { // Fallback for browsers without Intersection Observer this.loadImage(); } } private loadImage() { const img = this.el.nativeElement; const src = this.appLazyLoad || img.getAttribute('data-src'); if (src) { // Create a new image to preload const tempImg = new Image(); tempImg.onload = () => { this.renderer.setAttribute(img, 'src', src); this.renderer.removeClass(img, 'lazy-loading'); this.renderer.addClass(img, 'lazy-loaded'); // Disconnect observer after loading if (this.observer) { this.observer.disconnect(); } }; tempImg.onerror = () => { console.error('Failed to load image:', src); this.renderer.removeClass(img, 'lazy-loading'); this.renderer.addClass(img, 'lazy-error'); if (this.observer) { this.observer.disconnect(); } }; tempImg.src = src; } } ngOnDestroy() { if (this.observer) { this.observer.disconnect(); } } }