Skip to content

Instantly share code, notes, and snippets.

@cwgw
Created November 8, 2019 22:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cwgw/0646bedaa7bc326999aa889c28cfca51 to your computer and use it in GitHub Desktop.
Save cwgw/0646bedaa7bc326999aa889c28cfca51 to your computer and use it in GitHub Desktop.
import React from 'react';
import throttle from 'lodash/throttle';
let io;
let isInstantiated = false;
const listeners = new WeakMap();
const animationQueue = new Set([]);
const rootMargin = 100;
let pageYOffset = null;
const animate = throttle(
() => {
if (!animationQueue.size) return;
pageYOffset = window.pageYOffset;
animationQueue.forEach(callAnimation);
},
1000 / 30,
{ leading: true, trailing: true }
);
function useParallax (cb) {
if (typeof window === 'undefined') return null;
if (typeof window.IntersectionObserver !== 'function') return null;
const [ref, setRef] = React.useState(null);
React.useLayoutEffect(() => {
if (ref) {
listenToIntersections(ref, cb);
}
}, [ref]);
return setRef;
}
function getIO() {
if (
typeof io === `undefined` &&
typeof window !== `undefined` &&
window.IntersectionObserver
) {
io = new window.IntersectionObserver(
entries => {
entries.forEach(entry => {
if (listeners.has(entry.target)) {
const { cb } = listeners.get(entry.target);
// Edge doesn't currently support isIntersecting, so also test for an intersectionRatio > 0
if (entry.isIntersecting || entry.intersectionRatio > 0) {
listeners.set(entry.target, {
cb,
entry: {
range:
entry.rootBounds.height + entry.boundingClientRect.height,
center:
pageYOffset +
entry.boundingClientRect.top +
rootMargin -
entry.rootBounds.height,
},
});
animationQueue.add(entry.target);
animate();
} else {
animationQueue.delete(entry.target);
}
}
});
},
{ rootMargin: `${rootMargin}px`, threshold: 0 }
);
}
return io;
}
function listenToIntersections (el, cb) {
const observer = getIO();
if (observer) {
if (!isInstantiated) {
window.addEventListener('scroll', animate);
isInstantiated = true;
}
observer.observe(el);
listeners.set(el, { cb, entry: {} });
}
return () => {
observer.unobserve(el);
listeners.delete(el);
if (!listeners.size) {
window.removeEventListener('scroll', animate);
isInstantiated = false;
}
};
}
function callAnimation (el) {
const { cb, entry } = listeners.get(el);
const y = (pageYOffset - entry.center) / entry.range;
cb(y, { ref: entry.target });
}
export default useParallax;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment