Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 37 additions & 20 deletions projects/angular-cld/src/lib/cloudinary-lazy-load.directive.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,67 @@
import {AfterViewInit, Directive, ElementRef} from '@angular/core';
import {
AfterViewInit,
Directive,
ElementRef,
NgZone,
OnDestroy,
} from '@angular/core';

import { isBrowser } from './cloudinary.service';

// Check loading property is defined on image or iframe.
const isNativeLazyLoadSupported =
isBrowser && 'loading' in HTMLImageElement.prototype;
const isIntersectionObserverSupported =
isBrowser && 'IntersectionObserver' in window;

@Directive({
selector: 'cl-image[loading=lazy]'
selector: 'cl-image[loading=lazy]',
})
export class LazyLoadDirective implements AfterViewInit {
export class LazyLoadDirective implements AfterViewInit, OnDestroy {
private observer: IntersectionObserver | null = null;

constructor(private el: ElementRef) {}
constructor(private el: ElementRef, private ngZone: NgZone) {}

ngAfterViewInit() {
if (isBrowser()) {
if (!this.isNativeLazyLoadSupported() && this.isLazyLoadSupported()) {
this.lazyLoad();
if (!isNativeLazyLoadSupported && isIntersectionObserverSupported) {
this.lazyLoadThroughIntersectionObserver();
} else {
this.loadImage();
}
}
}

ngOnDestroy() {
if (this.observer) {
this.observer.disconnect();
this.observer = null;
}
}

loadImage() {
const nativeElement = this.el.nativeElement;
const image = nativeElement.children[0];
image.setAttribute('src', image.dataset.src);
}

isLazyLoadSupported() {
return window && 'IntersectionObserver' in window;
}

isNativeLazyLoadSupported() {
return 'loading' in HTMLImageElement.prototype; // check loading property is defined on image or iframe
}

lazyLoad() {
lazyLoadThroughIntersectionObserver() {
const options = {
rootMargin: `0px 0px -50% 0px`, // Margin around the root
};
const observer = new IntersectionObserver(
(entries) => {
entries.forEach(entry => {
// Caretaker note: the `IntersectionObserver` is patched by zone.js in the latest versions,
// which means it triggers change detection when `IntersectionObserver` tasks are invoked.
// We only set the `src` property within the task, so we don't require Angular running `tick()`.
this.ngZone.runOutsideAngular(() => {
this.observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
this.loadImage();
observer.unobserve(entry.target);
this.observer.unobserve(entry.target);
}
}, options);
});
observer.observe(this.el.nativeElement);
this.observer.observe(this.el.nativeElement);
});
}
}