78 lines
2.1 KiB
TypeScript
78 lines
2.1 KiB
TypeScript
import { DOCUMENT } from '@angular/common';
|
|
import {
|
|
afterNextRender,
|
|
DestroyRef,
|
|
Directive,
|
|
ElementRef,
|
|
inject,
|
|
Injector,
|
|
input,
|
|
} from '@angular/core';
|
|
import { isBrowserPlatform, prefersReducedMotion } from '../../core/platform/browser';
|
|
|
|
@Directive({
|
|
selector: '[appReveal]',
|
|
})
|
|
export class RevealDirective {
|
|
readonly appRevealThreshold = input(0.12);
|
|
|
|
private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);
|
|
private readonly document = inject(DOCUMENT);
|
|
private readonly injector = inject(Injector);
|
|
private readonly destroyRef = inject(DestroyRef);
|
|
private readonly isBrowser = isBrowserPlatform();
|
|
private readonly reducedMotion = prefersReducedMotion();
|
|
private observer: IntersectionObserver | null = null;
|
|
|
|
constructor() {
|
|
if (!this.isBrowser || this.reducedMotion || !this.canObserve()) {
|
|
this.revealNow();
|
|
return;
|
|
}
|
|
|
|
afterNextRender(() => this.observe(), { injector: this.injector });
|
|
this.destroyRef.onDestroy(() => this.disconnect());
|
|
}
|
|
|
|
private canObserve(): boolean {
|
|
const view = this.document.defaultView;
|
|
return !!view && typeof view.IntersectionObserver === 'function';
|
|
}
|
|
|
|
private observe(): void {
|
|
const view = this.document.defaultView;
|
|
|
|
if (!view || typeof view.IntersectionObserver !== 'function') {
|
|
this.revealNow();
|
|
return;
|
|
}
|
|
|
|
this.observer = new view.IntersectionObserver(
|
|
(entries) => {
|
|
if (entries.some((entry) => entry.isIntersecting)) {
|
|
this.host.nativeElement.classList.remove('reveal-pending');
|
|
this.host.nativeElement.classList.add('is-revealed');
|
|
this.disconnect();
|
|
return;
|
|
}
|
|
|
|
this.host.nativeElement.classList.add('reveal-pending');
|
|
},
|
|
{
|
|
rootMargin: '0px 0px -8% 0px',
|
|
threshold: this.appRevealThreshold(),
|
|
},
|
|
);
|
|
this.observer.observe(this.host.nativeElement);
|
|
}
|
|
|
|
private revealNow(): void {
|
|
this.host.nativeElement.classList.add('is-revealed');
|
|
}
|
|
|
|
private disconnect(): void {
|
|
this.observer?.disconnect();
|
|
this.observer = null;
|
|
}
|
|
}
|