diff --git a/src/index.ts b/src/index.ts index 0dffc73..93243d8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,75 +1,67 @@ -let props: (keyof DOMRect)[] = [ - "bottom", - "height", - "left", - "right", - "top", - "width", -]; +type RectProps = { + rect: DOMRect; + callbacks: Set; +}; -let rectChanged = (a: DOMRect = {} as DOMRect, b: DOMRect = {} as DOMRect) => - props.some((prop) => a[prop] !== b[prop]); +let COMPARE_KEYS = [ + "bottom", "height", "left", "right", "top", "width" +] as const; let observedNodes = new Map(); -let rafId: number; +let active: boolean; -let run = () => { - const changedStates: RectProps[] = []; - observedNodes.forEach((state, node) => { - let newRect = node.getBoundingClientRect(); - if (rectChanged(newRect, state.rect)) { - state.rect = newRect; - changedStates.push(state); - } - }); +function checkForUpdates(){ + if(active){ + observedNodes.forEach(assertDidUpdate); + window.requestAnimationFrame(checkForUpdates); + } +}; - changedStates.forEach((state) => { - state.callbacks.forEach((cb) => cb(state.rect)); - }); +function assertDidUpdate(state: RectProps, node: Element){ + let newRect = node.getBoundingClientRect(); - rafId = window.requestAnimationFrame(run); -}; + for(const key of COMPARE_KEYS) + if(newRect[key] !== state.rect[key]){ + state.rect = newRect; + state.callbacks.forEach(cb => cb(state.rect)) + break; + } +} export default function observeRect( - node: Element, - cb: (rect: DOMRect) => void -) { - return { - observe() { - let wasEmpty = observedNodes.size === 0; - if (observedNodes.has(node)) { - observedNodes.get(node)!.callbacks.push(cb); - } else { - observedNodes.set(node, { - rect: undefined, - hasRectChanged: false, - callbacks: [cb], - }); - } - if (wasEmpty) run(); - }, + node: Element, + callback: (rect: DOMRect) => void +){ + let state = observedNodes.get(node); - unobserve() { - let state = observedNodes.get(node); - if (state) { - // Remove the callback - const index = state.callbacks.indexOf(cb); - if (index >= 0) state.callbacks.splice(index, 1); + return { + observe(){ + if(state) + state.callbacks.add(callback); + else { + observedNodes.set(node, state = { + rect: {} as any, + callbacks: new Set([callback]), + }); - // Remove the node reference - if (!state.callbacks.length) observedNodes.delete(node); + if(!active){ + active = true; + checkForUpdates(); + } + } + }, + unobserve(){ + if(state){ + state.callbacks.delete(callback); - // Stop the loop - if (!observedNodes.size) cancelAnimationFrame(rafId); - } - }, - }; -} + if(!state.callbacks.size) + observedNodes.delete(node); -export type PartialRect = Partial; + state = undefined; -export type RectProps = { - rect: DOMRect | undefined; - hasRectChanged: boolean; - callbacks: Function[]; -}; + if(!observedNodes.size) + active = false; + } + } + } +} \ No newline at end of file