73 lines
2.0 KiB
TypeScript
73 lines
2.0 KiB
TypeScript
|
import {useEffect, useState} from 'preact/hooks';
|
||
|
import {JSX, RefObject} from 'preact';
|
||
|
|
||
|
|
||
|
export function useIntersectionObserver({
|
||
|
target,
|
||
|
onIntersect,
|
||
|
threshold = 0,
|
||
|
rootMargin = '0px',
|
||
|
enabled = true,
|
||
|
}:{
|
||
|
target: RefObject<Element>;
|
||
|
onIntersect: () => void;
|
||
|
threshold?: number;
|
||
|
rootMargin?: string;
|
||
|
enabled?: boolean;
|
||
|
}) {
|
||
|
|
||
|
useEffect(() => {
|
||
|
if (!enabled) {
|
||
|
return;
|
||
|
}
|
||
|
const observer = new IntersectionObserver(onIntersect, {
|
||
|
rootMargin,
|
||
|
threshold,
|
||
|
});
|
||
|
if (target.current) {
|
||
|
observer.observe(target.current);
|
||
|
}
|
||
|
|
||
|
return () => {
|
||
|
if (target.current) {
|
||
|
observer.unobserve(target.current);
|
||
|
}
|
||
|
};
|
||
|
}, [target.current, onIntersect, threshold, rootMargin, enabled]);
|
||
|
}
|
||
|
|
||
|
export function useRelativeTopOppacity({elem}:{elem: RefObject<Element>}) {
|
||
|
const [opacity, setOpacity] = useState(1);
|
||
|
|
||
|
useIntersectionObserver({
|
||
|
target: elem,
|
||
|
threshold: 0,
|
||
|
onIntersect: () => {
|
||
|
if (elem.current) {
|
||
|
addEventListener('scroll', updater);
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
return opacity;
|
||
|
function updater(){
|
||
|
if (elem.current) {
|
||
|
const el = elem.current;
|
||
|
const rect = el.getBoundingClientRect();
|
||
|
const upIntersect = Math.min(rect.bottom, el.clientHeight) / el.clientHeight;
|
||
|
const downIntersect = Math.min(window.innerHeight - rect.top, el.clientHeight) / el.clientHeight;
|
||
|
const intersect = Math.min(upIntersect, downIntersect);
|
||
|
|
||
|
if (intersect >= 0) {
|
||
|
let v = Math.min(Math.max(intersect, 0), 1);
|
||
|
v *= 4/3;
|
||
|
v = Math.min(Math.max(v, 0), 1);
|
||
|
setOpacity(v);
|
||
|
}
|
||
|
else{
|
||
|
setOpacity(1);
|
||
|
removeEventListener('scroll', updater);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|