* Observes an element's rectangle on screen (getBoundingClientRect) * This is useful to track elements on the screen and attach other elements * that might be in different layers, etc.
( /** The element whose rect to observe */ elementToObserve: Measurable, /** The callback which will be called when the rect changes */ callback: CallbackFn, )
| 6 | * that might be in different layers, etc. |
| 7 | */ |
| 8 | function observeElementRect( |
| 9 | /** The element whose rect to observe */ |
| 10 | elementToObserve: Measurable, |
| 11 | /** The callback which will be called when the rect changes */ |
| 12 | callback: CallbackFn, |
| 13 | ) { |
| 14 | const observedData = observedElements.get(elementToObserve) |
| 15 | |
| 16 | if (observedData === undefined) { |
| 17 | // add the element to the map of observed elements with its first callback |
| 18 | // because this is the first time this element is observed |
| 19 | observedElements.set(elementToObserve, { rect: {} as ClientRect, callbacks: [callback] }) |
| 20 | |
| 21 | if (observedElements.size === 1) { |
| 22 | // start the internal loop once at least 1 element is observed |
| 23 | rafId = requestAnimationFrame(runLoop) |
| 24 | } |
| 25 | } else { |
| 26 | // only add a callback for this element as it's already observed |
| 27 | observedData.callbacks.push(callback) |
| 28 | callback(elementToObserve.getBoundingClientRect()) |
| 29 | } |
| 30 | |
| 31 | return () => { |
| 32 | const observedData = observedElements.get(elementToObserve) |
| 33 | if (observedData === undefined) return |
| 34 | |
| 35 | // start by removing the callback |
| 36 | const index = observedData.callbacks.indexOf(callback) |
| 37 | if (index > -1) { |
| 38 | observedData.callbacks.splice(index, 1) |
| 39 | } |
| 40 | |
| 41 | if (observedData.callbacks.length === 0) { |
| 42 | // stop observing this element because there are no |
| 43 | // callbacks registered for it anymore |
| 44 | observedElements.delete(elementToObserve) |
| 45 | |
| 46 | if (observedElements.size === 0) { |
| 47 | // stop the internal loop once no elements are observed anymore |
| 48 | cancelAnimationFrame(rafId) |
| 49 | } |
| 50 | } |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | // ======================================================================== |
| 55 | // module internals |