framer-motion
Version:
A simple and powerful JavaScript animation library
84 lines (81 loc) • 3.16 kB
JavaScript
import { frame, cancelFrame, frameData } from 'motion-dom';
import { resize } from '../resize/index.mjs';
import { createScrollInfo } from './info.mjs';
import { createOnScrollHandler } from './on-scroll-handler.mjs';
const scrollListeners = new WeakMap();
const resizeListeners = new WeakMap();
const onScrollHandlers = new WeakMap();
const getEventTarget = (element) => element === document.documentElement ? window : element;
function scrollInfo(onScroll, { container = document.documentElement, ...options } = {}) {
let containerHandlers = onScrollHandlers.get(container);
/**
* Get the onScroll handlers for this container.
* If one isn't found, create a new one.
*/
if (!containerHandlers) {
containerHandlers = new Set();
onScrollHandlers.set(container, containerHandlers);
}
/**
* Create a new onScroll handler for the provided callback.
*/
const info = createScrollInfo();
const containerHandler = createOnScrollHandler(container, onScroll, info, options);
containerHandlers.add(containerHandler);
/**
* Check if there's a scroll event listener for this container.
* If not, create one.
*/
if (!scrollListeners.has(container)) {
const measureAll = () => {
for (const handler of containerHandlers)
handler.measure();
};
const updateAll = () => {
for (const handler of containerHandlers) {
handler.update(frameData.timestamp);
}
};
const notifyAll = () => {
for (const handler of containerHandlers)
handler.notify();
};
const listener = () => {
frame.read(measureAll, false, true);
frame.read(updateAll, false, true);
frame.update(notifyAll, false, true);
};
scrollListeners.set(container, listener);
const target = getEventTarget(container);
window.addEventListener("resize", listener, { passive: true });
if (container !== document.documentElement) {
resizeListeners.set(container, resize(container, listener));
}
target.addEventListener("scroll", listener, { passive: true });
}
const listener = scrollListeners.get(container);
frame.read(listener, false, true);
return () => {
cancelFrame(listener);
/**
* Check if we even have any handlers for this container.
*/
const currentHandlers = onScrollHandlers.get(container);
if (!currentHandlers)
return;
currentHandlers.delete(containerHandler);
if (currentHandlers.size)
return;
/**
* If no more handlers, remove the scroll listener too.
*/
const scrollListener = scrollListeners.get(container);
scrollListeners.delete(container);
if (scrollListener) {
getEventTarget(container).removeEventListener("scroll", scrollListener);
resizeListeners.get(container)?.();
window.removeEventListener("resize", scrollListener);
}
};
}
export { scrollInfo };