Rumah  >  Artikel  >  hujung hadapan web  >  Angular LAB: mari buat Arahan Keterlihatan

Angular LAB: mari buat Arahan Keterlihatan

Barbara Streisand
Barbara Streisandasal
2024-10-09 08:21:02934semak imbas

Angular LAB: let

Dalam artikel ini saya akan menggambarkan cara mencipta Arahan Sudut yang sangat mudah yang menjejaki keadaan keterlihatan elemen, atau dengan kata lain, apabila ia masuk dan keluar dari tempat pandang. Saya harap ini akan menjadi latihan yang bagus dan mungkin berguna!

Untuk melakukan ini, kami akan menggunakan IntersectionObserver JavaScript API yang tersedia dalam penyemak imbas moden.

Apa yang kita nak capai

Kami mahu menggunakan Arahan seperti ini:

<p
  visibility
  [visibilityMonitor]="true"
  (visibilityChange)="onVisibilityChange($event)"
>
  I'm being observed! Can you see me yet?
</p>
  • keterlihatan ialah pemilih arahan tersuai kami
  • visibilityMonitor ialah input pilihan yang menentukan sama ada untuk terus memerhati elemen atau tidak (jika palsu, hentikan pemantauan apabila ia memasuki port pandangan)
  • visibilityChange akan memberitahu kami

Output akan dalam bentuk ini:

type VisibilityChange =
  | {
      isVisible: true;
      target: HTMLElement;
    }
  | {
      isVisible: false;
      target: HTMLElement | undefined;
    };

Mempunyai sasaran yang tidak ditentukan bermakna elemen telah dialih keluar daripada DOM (contohnya, oleh @if).

Penciptaan Arahan

Arahan kami hanya akan memantau elemen, ia tidak akan mengubah struktur DOM: ia akan menjadi Arahan Atribut.

@Directive({
  selector: "[visibility]",
  standalone: true
})
export class VisibilityDirective implements OnInit, OnChanges, AfterViewInit, OnDestroy {
  private element = inject(ElementRef);

  /**
   * Emits after the view is initialized.
   */
  private afterViewInit$ = new Subject<void>();

  /**
   * The IntersectionObserver for this element.
   */
  private observer: IntersectionObserver | undefined;

  /**
   * Last known visibility for this element.
   * Initially, we don't know.
   */
  private isVisible: boolean = undefined;

  /**
   * If false, once the element becomes visible there will be one emission and then nothing.
   * If true, the directive continuously listens to the element and emits whenever it becomes visible or not visible.
   */
  visibilityMonitor = input(false);

  /**
   * Notifies the listener when the element has become visible.
   * If "visibilityMonitor" is true, it continuously notifies the listener when the element goes in/out of view.
   */
  visibilityChange = output<VisibilityChange>();
}

Dalam kod di atas anda lihat:

  • input dan output yang kita bincangkan tadi
  • sifat yang dipanggil afterViewInit$ (an Observable) yang akan bertindak sebagai rakan reaktif kepada cangkuk kitar hayat ngAfterViewInit
  • harta yang dipanggil observer yang akan menyimpan IntersectionObserver yang bertanggungjawab memantau elemen kami
  • sifat yang dipanggil isVisibile yang akan menyimpan keadaan keterlihatan terakhir, untuk mengelakkan daripada memancarkan semula keadaan yang sama dua kali berturut-turut

Dan secara semulajadi, kami menyuntik ElementRef untuk mendapatkan elemen DOM yang kami gunakan arahan kami.

Sebelum menulis kaedah utama, mari kita jaga kitaran hayat arahan.

ngOnInit(): void {
  this.reconnectObserver();
}

ngOnChanges(): void {
  this.reconnectObserver();
}

ngAfterViewInit(): void {
  this.afterViewInit$.next();
}

ngOnDestroy(): void {
  // Disconnect and if visibilityMonitor is true, notify the listener
  this.disconnectObserver();
  if (this.visibilityMonitor) {
    this.visibilityChange.emit({
      isVisible: false,
      target: undefined
    });
  }
}

private reconnectObserver(): void {}
private disconnectObserver(): void {}

Sekarang inilah yang berlaku:

  • Di dalam kedua-dua ngOnInit dan ngOnChanges kami memulakan semula pemerhati. Ini adalah untuk menjadikan arahan itu reaktif: jika input berubah, arahan akan mula berkelakuan berbeza. Perhatikan bahawa, walaupun ngOnChanges juga dijalankan sebelum ngOnInit, kami masih memerlukan ngOnInit kerana ngOnChanges tidak berjalan jika tiada input dalam templat!
  • Apabila paparan dimulakan, kami mencetuskan Subjek, kami akan sampai ke perkara ini dalam beberapa saat
  • Kami memutuskan sambungan pemerhati kami apabila arahan dimusnahkan untuk mengelakkan kebocoran memori. Akhir sekali, jika pembangun memintanya, kami memberitahu bahawa elemen telah dialih keluar daripada DOM dengan memancarkan elemen yang tidak ditentukan.

Pemerhati Persimpangan

Ini adalah inti dari arahan kami. Kaedah reconnectObserver kami akan mula memerhati! Ia akan menjadi seperti ini:

private reconnectObserver(): void {
    // Disconnect an existing observer
    this.disconnectObserver();
    // Sets up a new observer
    this.observer = new IntersectionObserver((entries, observer) => {
      entries.forEach(entry => {
        const { isIntersecting: isVisible, target } = entry;
        const hasChangedVisibility = isVisible !== this.isVisible;
        const shouldEmit = isVisible || (!isVisible && this.visibilityMonitor);
        if (hasChangedVisibility && shouldEmit) {
          this.visibilityChange.emit({
            isVisible,
            target: target as HTMLElement
          });
          this.isVisible = isVisible;
        }
        // If visilibilyMonitor is false, once the element is visible we stop.
        if (isVisible && !this.visibilityMonitor) {
          observer.disconnect();
        }
      });
    });
    // Start observing once the view is initialized
    this.afterViewInit$.subscribe(() => {
        this.observer?.observe(this.element.nativeElement);
    });
  }

Percayalah, ia tidaklah serumit yang kelihatan! Inilah mekanismenya:

  • Mula-mula kita putuskan sambungan pemerhati jika ia sudah berjalan
  • Kami mencipta IntersectionObserver dan menentukan kelakuannya. Entri akan mengandungi elemen yang dipantau, jadi ia akan mengandungi elemen kami. Sifat isInterecting akan menunjukkan jika keterlihatan elemen telah berubah: kami membandingkannya dengan keadaan sebelumnya (harta kami) dan jika ia perlu dibayar, kami mengeluarkan. Kemudian kita simpan keadaan baru dalam harta kita untuk kemudian.
  • Jika visibilityMonitor palsu, sebaik sahaja elemen kelihatan, kami memutuskan sambungan pemerhati: tugasnya selesai!
  • Kemudian kita perlu mulakan pemerhati dengan melepasi elemen kami, jadi kami menunggu pandangan kami dimulakan untuk melakukannya.

Akhir sekali, mari kita laksanakan kaedah yang memutuskan sambungan pemerhati, easy peasy:

 private disconnectObserver(): void {
    if (this.observer) {
      this.observer.disconnect();
      this.observer = undefined;
    }
  }

Kod akhir

Ini arahan penuh. Ini hanyalah latihan, jadi bebas untuk menukarnya kepada apa sahaja yang anda suka!

type VisibilityChange =
  | {
      isVisible: true;
      target: HTMLElement;
    }
  | {
      isVisible: false;
      target: HTMLElement | undefined;
    };

@Directive({
  selector: "[visibility]",
  standalone: true
})
export class VisibilityDirective
  implements OnChanges, OnInit, AfterViewInit, OnDestroy {
  private element = inject(ElementRef);

  /**
   * Emits after the view is initialized.
   */
  private afterViewInit$ = new Subject();

  /**
   * The IntersectionObserver for this element.
   */
  private observer: IntersectionObserver | undefined;

  /**
   * Last known visibility for this element.
   * Initially, we don't know.
   */
  private isVisible: boolean = undefined;

  /**
   * If false, once the element becomes visible there will be one emission and then nothing.
   * If true, the directive continuously listens to the element and emits whenever it becomes visible or not visible.
   */
  visibilityMonitor = input(false);

  /**
   * Notifies the listener when the element has become visible.
   * If "visibilityMonitor" is true, it continuously notifies the listener when the element goes in/out of view.
   */
  visibilityChange = output();

  ngOnInit(): void {
    this.reconnectObserver();
  }

  ngOnChanges(): void {
    this.reconnectObserver();
  }

  ngAfterViewInit(): void {
    this.afterViewInit$.next(true);
  }

  ngOnDestroy(): void {
    // Disconnect and if visibilityMonitor is true, notify the listener
    this.disconnectObserver();
    if (this.visibilityMonitor) {
      this.visibilityChange.emit({
        isVisible: false,
        target: undefined
      });
    }
  }

  private reconnectObserver(): void {
    // Disconnect an existing observer
    this.disconnectObserver();
    // Sets up a new observer
    this.observer = new IntersectionObserver((entries, observer) => {
      entries.forEach(entry => {
        const { isIntersecting: isVisible, target } = entry;
        const hasChangedVisibility = isVisible !== this.isVisible;
        const shouldEmit = isVisible || (!isVisible && this.visibilityMonitor);
        if (hasChangedVisibility && shouldEmit) {
          this.visibilityChange.emit({
            isVisible,
            target: target as HTMLElement
          });
          this.isVisible = isVisible;
        }
        // If visilibilyMonitor is false, once the element is visible we stop.
        if (isVisible && !this.visibilityMonitor) {
          observer.disconnect();
        }
      });
    });
    // Start observing once the view is initialized
    this.afterViewInit$.subscribe(() => {
        this.observer?.observe(this.element.nativeElement);
    });
  }

  private disconnectObserver(): void {
    if (this.observer) {
      this.observer.disconnect();
      this.observer = undefined;
    }
  }
}

Atas ialah kandungan terperinci Angular LAB: mari buat Arahan Keterlihatan. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn