import IntersectionObserver, { IntersectionObserverOptions, IntersectionObserverEntry, Element, } from './IntersectionObserver'; export type ObserverInstanceCallback = (inView: boolean, intersectionRatio: number) => void; export interface ObserverInstance { readonly callback: ObserverInstanceCallback; readonly element: Element; readonly observerId: number; readonly observer: IntersectionObserver; } /** * @kind class * @name IOManager * @description `IntersectionObserver` 인스턴스를 사용해서 DOM 요소의 가시성을 추적하고, 요소가 뷰포트에 들어오거나 나갈 때 콜백을 실행하는 클래스예요. * 이 클래스를 통해 여러 요소를 손쉽게 관리할 수 있고, 각 요소의 가시성 상태에 따라 맞춤형 로직을 실행할 수 있어요. */ export class IOManager { io: IntersectionObserver; observerId: number; instanceMap: Map = new Map(); constructor(options: IntersectionObserverOptions) { this.io = new IntersectionObserver(this.handleChange, options); this.observerId = 0; } handleChange = (entries: IntersectionObserverEntry[]) => { for (let index = 0; index < entries.length; index += 1) { const entry = entries[index]; if (entry == null) { continue; } const { target, isIntersecting, intersectionRatio } = entry; const instance = this.instanceMap.get(target); if (instance) { instance.callback(isIntersecting, intersectionRatio); } } }; observe(element: Element, callback: ObserverInstanceCallback): ObserverInstance { const existInstance = this.instanceMap.get(element); if (existInstance) { return existInstance; } this.observerId += 1; const instance: ObserverInstance = { callback, element, observerId: this.observerId, observer: this.io, }; this.instanceMap.set(element, instance); this.io.observe(element); return instance; } unobserve(element: any) { if (this.instanceMap.has(element)) { this.instanceMap.delete(element); this.io.unobserve(element); } } }