|
import { |
|
Directive, |
|
ElementRef, |
|
EventEmitter, |
|
Input, |
|
OnInit, |
|
OnChanges, |
|
SimpleChanges, |
|
OnDestroy, |
|
Output, |
|
inject, |
|
} from '@angular/core'; |
|
|
|
@Directive({ |
|
selector: '[appIntersectionObserver]', |
|
standalone: true, |
|
}) |
|
export class IntersectionObserverDirective implements OnInit, OnChanges, OnDestroy { |
|
private readonly elementRef = inject(ElementRef<HTMLElement>); |
|
|
|
@Input() threshold: number | number[] = 0.1; |
|
@Input() rootMargin = '0px'; |
|
@Input() once = false; |
|
|
|
@Output() visible = new EventEmitter<IntersectionObserverEntry>(); |
|
@Output() hidden = new EventEmitter<IntersectionObserverEntry>(); |
|
|
|
private observer: IntersectionObserver | null = null; |
|
private initialized = false; |
|
|
|
ngOnInit(): void { |
|
this.initialized = true; |
|
this.createObserver(); |
|
} |
|
|
|
ngOnChanges(changes: SimpleChanges): void { |
|
if (!this.initialized) { |
|
return; |
|
} |
|
|
|
const thresholdChanged = |
|
changes['threshold'] && |
|
changes['threshold'].previousValue !== changes['threshold'].currentValue; |
|
|
|
const rootMarginChanged = |
|
changes['rootMargin'] && |
|
changes['rootMargin'].previousValue !== changes['rootMargin'].currentValue; |
|
|
|
if (thresholdChanged || rootMarginChanged) { |
|
this.createObserver(); |
|
} |
|
} |
|
|
|
ngOnDestroy(): void { |
|
this.disconnectObserver(); |
|
} |
|
|
|
private createObserver(): void { |
|
this.disconnectObserver(); |
|
|
|
if (typeof IntersectionObserver === 'undefined') { |
|
return; |
|
} |
|
|
|
this.observer = new IntersectionObserver( |
|
(entries) => { |
|
for (const entry of entries) { |
|
if (entry.isIntersecting) { |
|
this.visible.emit(entry); |
|
|
|
if (this.once) { |
|
this.disconnectObserver(); |
|
return; |
|
} |
|
} else { |
|
this.hidden.emit(entry); |
|
} |
|
} |
|
}, |
|
{ |
|
threshold: this.threshold, |
|
rootMargin: this.rootMargin, |
|
} |
|
); |
|
|
|
this.observer.observe(this.elementRef.nativeElement); |
|
} |
|
|
|
private disconnectObserver(): void { |
|
this.observer?.disconnect(); |
|
this.observer = null; |
|
} |
|
} |